Android Open Source - GeoLog Exporter






From Project

Back to project page GeoLog.

License

The source code is released under:

Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUC...

If you think the Android project GeoLog listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.

Java Source Code

/*
 * Copyright (C) 2013 Jorrit "Chainfire" Jongma
 *//from   w ww. jav a  2s. c o m
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package eu.chainfire.geolog.data;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;

import eu.chainfire.geolog.Application;
import eu.chainfire.geolog.Debug;
import eu.chainfire.geolog.R;
import eu.chainfire.geolog.data.Database.*;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.os.AsyncTask;
import android.text.Html;

public class Exporter {
  public static enum Format { GPX, KML };

  private interface OnExportProgressListener {
    public void OnExportProgress(int cur, int total);
  }
      
  private final Context context;
  
  private Format format = Format.GPX;
  private long trackMergeGap = 0;
  private long dateStart = -1;
  private long dateEnd = -1;
  private long trackMinPoints = 0;
  private long trackMinTime = 0;
  private long trackMinDistance = 0;
  private long accuracyLowPowerUnknown = 0;
  private long accuracyLowPowerStill = 0;
  private long accuracyLowPowerFoot = 0;
  private long accuracyLowPowerBicycle = 0;
  private long accuracyLowPowerVehicle = 0;
  private long accuracyHighAccuracyUnknown = 0;
  private long accuracyHighAccuracyStill = 0;
  private long accuracyHighAccuracyFoot = 0;
  private long accuracyHighAccuracyBicycle = 0;
  private long accuracyHighAccuracyVehicle = 0;
  
  private int inSegment = 0;
  private boolean isSegmentStart = false;
  private long lastTime = -1;          
  private long lastTrackStartBytes = 0;
  private long lastTrackStartTime = 0;
  private double trackLatMin = 0;
  private double trackLatMax = 0;
  private double trackLongMin = 0;
  private double trackLongMax = 0;  
  
  public Exporter(Context context) {
    this.context = context;
  }

  public void export(Cursor cursor) {
    if (context instanceof Activity) {
      (new ExportAsync()).execute(cursor);
    } else {
      performExport(null, cursor);
    }
  }
  
  private double gps2m(double lat_a, double lng_a, double lat_b, double lng_b) {
      double pk = (double) (180/Math.PI);

      double a1 = lat_a / pk;
      double a2 = lng_a / pk;
      double b1 = lat_b / pk;
      double b2 = lng_b / pk;

      double t1 = Math.cos(a1)*Math.cos(a2)*Math.cos(b1)*Math.cos(b2);
      double t2 = Math.cos(a1)*Math.sin(a2)*Math.cos(b1)*Math.sin(b2);
      double t3 = Math.sin(a1)*Math.sin(b1);
      double tt = Math.acos(t1 + t2 + t3);

      return 6366000*tt;
  }  
  
  private boolean shouldCancel() {
    boolean cancel = false;
    
    if ((trackMinPoints > 0) && (inSegment < trackMinPoints)) {
      Debug.log(String.format(Locale.ENGLISH, "CANCEL POINTS %d < %d", inSegment, trackMinPoints));
      cancel = true;                      
    }
    
    if ((trackMinTime > 0) && (lastTime - lastTrackStartTime < trackMinTime * 1000)) { 
      Debug.log(String.format(Locale.ENGLISH, "CANCEL TIME %d < %d", (int)((lastTime - lastTrackStartTime) / 1000), trackMinTime));
      cancel = true;
    }
    
    if (trackMinDistance > 0) {
      double m = gps2m(trackLatMin, trackLongMin, trackLatMax, trackLongMax);
      if (m < trackMinDistance) {
        Debug.log(String.format(Locale.ENGLISH, "CANCEL DISTANCE %d < %d", (int)m, trackMinDistance));
        cancel = true;                        
      }                      
    }    
    
    return cancel;
  }
      
  private String performExport(OnExportProgressListener callback, Cursor cursor) {
    String filename = "";
    switch (format) {
    case GPX: filename = Application.SDCARD_PATH + "/geolog.gpx"; break;
    case KML: filename = Application.SDCARD_PATH + "/geolog.kml"; break;
    }
    (new File(filename)).delete();
        
    try {
      FileOutputStream fos = new FileOutputStream(filename, false);      
      try {
        FormatWriter writer = null;
        switch (format) {
        case GPX: writer = new GPXWriter(fos); break;
        case KML: writer = new KMLWriter(fos); break;
        }
        
        String exporter = "GeoLog";        
          
        PackageManager pm = context.getPackageManager();
          if (pm != null) {
            try {
              PackageInfo pi = pm.getPackageInfo(context.getPackageName(), 0);
              if (pi != null) {
                exporter += " v" + pi.versionName;
              }
            } catch (Exception e) {
            }
          }
          
          // only used for debug logging - exported is in writer
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.ENGLISH);
        simpleDateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));      
                    
          writer.header(exporter);
                    
          lastTrackStartBytes = fos.getChannel().position();    
          writer.startSegment();

          Cursor c = cursor;
          if ((c != null) && (c.getCount() > 0)) {
            Database.Location loc = new Database.Location();
            
            c.moveToFirst();
            int index = 0;
            int count = c.getCount();
            while (true) {
              loc.loadFromCursor(c);
              
              Debug.log(String.format(Locale.ENGLISH, "WRITE %d %.5f %.5f %s", index, loc.getLatitude(), loc.getLongitude(), simpleDateFormat.format(new Date(loc.getTime()))));
              
              if (lastTrackStartTime == 0) {
                lastTrackStartTime = loc.getTime();
                trackLatMin = loc.getLatitude();
                trackLatMax = loc.getLatitude();
                trackLongMin = loc.getLongitude();
                trackLongMax = loc.getLongitude();
              }
              isSegmentStart = isSegmentStart || loc.isSegmentStart(); // carries over in case not used
              
              boolean ok = 
                (loc.getAccuracySetting() == Accuracy.NONE) ||
                ((loc.getAccuracySetting() == Accuracy.LOW) && (                  
                  ((loc.getActivity() == Database.Activity.UNKNOWN) && ((accuracyLowPowerUnknown <= 0) || loc.getAccuracyDistance() <= accuracyLowPowerUnknown)) ||
                  ((loc.getActivity() == Database.Activity.STILL) && ((accuracyLowPowerStill <= 0) || loc.getAccuracyDistance() <= accuracyLowPowerStill)) ||
                  ((loc.getActivity() == Database.Activity.FOOT) && ((accuracyLowPowerFoot <= 0) || loc.getAccuracyDistance() <= accuracyLowPowerFoot)) ||
                  ((loc.getActivity() == Database.Activity.BICYCLE) && ((accuracyLowPowerBicycle <= 0) || loc.getAccuracyDistance() <= accuracyLowPowerBicycle)) ||
                  ((loc.getActivity() == Database.Activity.VEHICLE) && ((accuracyLowPowerVehicle <= 0) || loc.getAccuracyDistance() <= accuracyLowPowerVehicle))
                )) ||
                ((loc.getAccuracySetting() == Accuracy.HIGH) && (
                  ((loc.getActivity() == Database.Activity.UNKNOWN) && ((accuracyHighAccuracyUnknown <= 0) || loc.getAccuracyDistance() <= accuracyHighAccuracyUnknown)) ||
                  ((loc.getActivity() == Database.Activity.STILL) && ((accuracyHighAccuracyStill <= 0) || loc.getAccuracyDistance() <= accuracyHighAccuracyStill)) ||
                  ((loc.getActivity() == Database.Activity.FOOT) && ((accuracyHighAccuracyFoot <= 0) || loc.getAccuracyDistance() <= accuracyHighAccuracyFoot)) ||
                  ((loc.getActivity() == Database.Activity.BICYCLE) && ((accuracyHighAccuracyBicycle <= 0) || loc.getAccuracyDistance() <= accuracyHighAccuracyBicycle)) ||
                  ((loc.getActivity() == Database.Activity.VEHICLE) && ((accuracyHighAccuracyVehicle <= 0) || loc.getAccuracyDistance() <= accuracyHighAccuracyVehicle))
                ));

              if (ok) {
                if (isSegmentStart) {
                  if (loc.getTime() - lastTime < trackMergeGap * 1000) {
                    Debug.log(String.format(Locale.ENGLISH, "MERGE %d %ds", index, (int)((loc.getTime() - lastTime) / 1000)));
                    
                    isSegmentStart = false;
                  }
                }
                  
                if (isSegmentStart) {
                  if (inSegment > 0) {                                        
                    if (shouldCancel()) {
                      fos.getChannel().position(lastTrackStartBytes);
                      fos.getChannel().truncate(lastTrackStartBytes);                      
                    } else {
                      writer.endSegment();
                    }
                    Debug.log(String.format(Locale.ENGLISH, "TRACK %d", index));
                    lastTrackStartBytes = fos.getChannel().position();
                      writer.startSegment();
                    
                    lastTrackStartTime = loc.getTime();
                    trackLatMin = loc.getLatitude();
                    trackLatMax = loc.getLatitude();
                    trackLongMin = loc.getLongitude();
                    trackLongMax = loc.getLongitude();

                    inSegment = 0;
                  }
                  isSegmentStart = false;
                }

                // if !ok we don't know location is correct, so we only do this here
                trackLatMin = Math.min(trackLatMin, loc.getLatitude());              
                trackLatMax = Math.max(trackLatMax, loc.getLatitude());
                trackLongMin = Math.min(trackLongMin, loc.getLongitude());
                trackLongMax = Math.max(trackLongMax, loc.getLongitude());
                
                writer.point(loc);
                inSegment++;
              } else {
                Debug.log(String.format(Locale.ENGLISH, "SKIP %d %dm", index, (int)loc.getAccuracyDistance()));                
              }
              
              lastTime = loc.getTime(); // take into account even if we don't store point, because we know time is correct
              
              index++;
              if (callback != null) callback.OnExportProgress(index, count);
              if (!c.moveToNext()) break;
            }
          }
          
        if ((inSegment == 0) || shouldCancel()) {
          fos.getChannel().position(lastTrackStartBytes);
          fos.getChannel().truncate(lastTrackStartBytes);                      
        } else {
          writer.endSegment();
        }          

          writer.footer();
      } finally {
        fos.close();
      }
    } catch (Exception e) {
      e.printStackTrace();
      (new File(filename)).delete();
      return null;
    }
    return filename;
  }
  
  private class ExportAsync extends AsyncTask<Cursor, Integer, String> {
    private ProgressDialog dialog = null;

    @Override
    protected void onPreExecute() {
      dialog = new ProgressDialog(context);    
      dialog.setTitle(R.string.export_exporting);
      dialog.setIndeterminate(false);
      dialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
      dialog.setCancelable(false);
      dialog.setProgress(0);
      dialog.setMax(1);
      dialog.show();
    }

    @Override
    protected void onProgressUpdate(Integer... values) {
      dialog.setMax(values[1]);
      dialog.setProgress(values[0]);
    }

    @Override
    protected String doInBackground(Cursor... params) {
      return performExport(new OnExportProgressListener() {        
        @Override
        public void OnExportProgress(int cur, int total) {
          publishProgress(cur, total);
        }
      }, params[0]);
    }
    
    @Override
    protected void onPostExecute(String result) {
      dialog.dismiss();
      
      (new AlertDialog.Builder(context)).
        setTitle(R.string.export_export).
        setMessage(Html.fromHtml(context.getString((result == null) ? R.string.export_failed : R.string.export_complete))).
        setPositiveButton(R.string.generic_ok, null).
        show();
    }        
  }
  
  public Format getFormat() {
    return format;
  }

  public void setFormat(Format format) {
    this.format = format;
  }

  public long getTrackMergeGap() {
    return trackMergeGap;
  }

  public void setTrackMergeGap(long trackMergeGap) {
    this.trackMergeGap = trackMergeGap;
  }

  public long getDateStart() {
    return dateStart;
  }

  public void setDateStart(long dateStart) {
    this.dateStart = dateStart;
  }

  public long getDateEnd() {
    return dateEnd;
  }

  public void setDateEnd(long dateEnd) {
    this.dateEnd = dateEnd;
  }

  public long getAccuracyLowPowerUnknown() {
    return accuracyLowPowerUnknown;
  }

  public void setAccuracyLowPowerUnknown(long accuracyLowPowerUnknown) {
    this.accuracyLowPowerUnknown = accuracyLowPowerUnknown;
  }

  public long getAccuracyLowPowerStill() {
    return accuracyLowPowerStill;
  }

  public void setAccuracyLowPowerStill(long accuracyLowPowerStill) {
    this.accuracyLowPowerStill = accuracyLowPowerStill;
  }

  public long getAccuracyLowPowerFoot() {
    return accuracyLowPowerFoot;
  }

  public void setAccuracyLowPowerFoot(long accuracyLowPowerFoot) {
    this.accuracyLowPowerFoot = accuracyLowPowerFoot;
  }

  public long getAccuracyLowPowerBicycle() {
    return accuracyLowPowerBicycle;
  }

  public void setAccuracyLowPowerBicycle(long accuracyLowPowerBicycle) {
    this.accuracyLowPowerBicycle = accuracyLowPowerBicycle;
  }

  public long getAccuracyLowPowerVehicle() {
    return accuracyLowPowerVehicle;
  }

  public void setAccuracyLowPowerVehicle(long accuracyLowPowerVehicle) {
    this.accuracyLowPowerVehicle = accuracyLowPowerVehicle;
  }

  public long getAccuracyHighAccuracyUnknown() {
    return accuracyHighAccuracyUnknown;
  }

  public void setAccuracyHighAccuracyUnknown(long accuracyHighAccuracyUnknown) {
    this.accuracyHighAccuracyUnknown = accuracyHighAccuracyUnknown;
  }

  public long getAccuracyHighAccuracyStill() {
    return accuracyHighAccuracyStill;
  }

  public void setAccuracyHighAccuracyStill(long accuracyHighAccuracyStill) {
    this.accuracyHighAccuracyStill = accuracyHighAccuracyStill;
  }

  public long getAccuracyHighAccuracyFoot() {
    return accuracyHighAccuracyFoot;
  }

  public void setAccuracyHighAccuracyFoot(long accuracyHighAccuracyFoot) {
    this.accuracyHighAccuracyFoot = accuracyHighAccuracyFoot;
  }

  public long getAccuracyHighAccuracyBicycle() {
    return accuracyHighAccuracyBicycle;
  }

  public void setAccuracyHighAccuracyBicycle(long accuracyHighAccuracyBicycle) {
    this.accuracyHighAccuracyBicycle = accuracyHighAccuracyBicycle;
  }

  public long getAccuracyHighAccuracyVehicle() {
    return accuracyHighAccuracyVehicle;
  }

  public void setAccuracyHighAccuracyVehicle(long accuracyHighAccuracyVehicle) {
    this.accuracyHighAccuracyVehicle = accuracyHighAccuracyVehicle;
  }

  public long getTrackMinPoints() {
    return trackMinPoints;
  }

  public void setTrackMinPoints(long trackMinPoints) {
    this.trackMinPoints = trackMinPoints;
  }

  public long getTrackMinTime() {
    return trackMinTime;
  }

  public void setTrackMinTime(long trackMinTime) {
    this.trackMinTime = trackMinTime;
  }

  public long getTrackMinDistance() {
    return trackMinDistance;
  }

  public void setTrackMinDistance(long trackMinDistance) {
    this.trackMinDistance = trackMinDistance;
  }

  private abstract class FormatWriter {
    protected OutputStream os = null;
    
    public FormatWriter(OutputStream os) {
      this.os = os;
    }
    
    protected void write(String string) throws UnsupportedEncodingException, IOException {
      os.write(string.getBytes("US-ASCII"));
    }
    
    public abstract void header(String exporter) throws UnsupportedEncodingException, IOException;
    public abstract void startSegment() throws UnsupportedEncodingException, IOException;
    public abstract void point(Database.Location loc) throws UnsupportedEncodingException, IOException;
    public abstract void endSegment() throws UnsupportedEncodingException, IOException;
    public abstract void footer() throws UnsupportedEncodingException, IOException;
  }
  
  private class GPXWriter extends FormatWriter {
    protected SimpleDateFormat simpleDateFormat = null;
        
    public GPXWriter(OutputStream os) {
      super(os);
      simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.ENGLISH);
      simpleDateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));      
    }

    @Override
    public void header(String exporter) throws UnsupportedEncodingException, IOException {
      write(
             "<?xml version=\"1.0\"?>\r\n" +
             "<gpx version=\"1.0\" creator=\"" + exporter + "\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns=\"http://www.topografix.com/GPX/1/0\" xsi:schemaLocation=\"http://www.topografix.com/GPX/1/0 http://www.topografix.com/GPX/1/0/gpx.xsd\">\r\n" +
             "  <trk>\r\n"
          );                        
    }

    @Override
    public void startSegment() throws UnsupportedEncodingException, IOException {
        write(
           "    <trkseg>\r\n"
        );
    }

    @Override
    public void point(Location loc) throws UnsupportedEncodingException, IOException {
      write(String.format(Locale.ENGLISH, "      <trkpt lat=\"%.5f\" lon=\"%.5f\"><time>%s</time></trkpt>\r\n", loc.getLatitude(), loc.getLongitude(), simpleDateFormat.format(new Date(loc.getTime()))));
    }    

    @Override
    public void endSegment() throws UnsupportedEncodingException, IOException {
      write(
        "    </trkseg>\r\n"
      );
    }

    @Override
    public void footer() throws UnsupportedEncodingException, IOException {
        write(
          "  </trk>\r\n" +
          "</gpx>\r\n"
        );        
    }
  }  
  
  private class KMLWriter extends FormatWriter {
    protected int trackIndex = 1;
    protected String last = "";
    
    public KMLWriter(OutputStream os) {
      super(os);      
    }
    
    @Override
    public void header(String exporter)  throws UnsupportedEncodingException, IOException {
      write(
        "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n" +
        "<kml xmlns=\"http://www.opengis.net/kml/2.2\"  xmlns:gx=\"http://www.google.com/kml/ext/2.2\" xmlns:kml=\"http://www.opengis.net/kml/2.2\"\r\n" +    
        "  xmlns:atom=\"http://www.w3.org/2005/Atom\">\r\n" +
        "  <Document>\r\n" +
        "    <name>Exported by " + exporter + "</name>\r\n" +
        "    <description></description>\r\n" +
        "    <visibility>1</visibility>\r\n" +
        "    <open>1</open>\r\n" +
        "    <Style id=\"red\"><LineStyle><color>C81400FF</color><width>4</width></LineStyle></Style>\r\n" +          
        "    <Folder>\r\n" +
        "      <name>Tracks</name>\r\n" +
        "      <description></description>\r\n" +
        "      <visibility>1</visibility>\r\n" +            
        "      <open>1</open>\r\n"        
          );
    }

    @Override
    public void startSegment() throws UnsupportedEncodingException,  IOException {
      write(
          "      <Placemark>\r\n" +
          "        <visibility>1</visibility>\r\n" +            
          "        <open>1</open>\r\n" + 
          "        <styleUrl>#red</styleUrl>\r\n" +
          "        <name>Track " + String.valueOf(trackIndex) + "</name>\r\n" +
          "        <description></description>\r\n" +
          "        <LineString>\r\n" +
          "          <extrude>true</extrude>\r\n" +
          "          <tessellate>true</tessellate>\r\n" +
          "          <altitudeMode>clampToGround</altitudeMode>\r\n" + 
          "            <coordinates>\r\n"
      );
      trackIndex++;
    }

    @Override
    public void point(Location loc) throws UnsupportedEncodingException, IOException {
      String now = String.format(Locale.ENGLISH, "            %.5f,%.5f\r\n", loc.getLongitude(), loc.getLatitude());
      if (!now.equals(last)) {
        write(now);
        last = now;
      }
    }

    @Override
    public void endSegment() throws UnsupportedEncodingException, IOException {
      write(
        "          </coordinates>\r\n" +
        "        </LineString>\r\n" +
        "      </Placemark>\r\n"
      );      
    }

    @Override
    public void footer() throws UnsupportedEncodingException, IOException {
      write(
        "    </Folder>\r\n" +
          "  </Document>\r\n" +
          "</kml>\r\n"
      );
    }
  }
}




Java Source Code List

eu.chainfire.geolog.Application.java
eu.chainfire.geolog.CrashCatcher.java
eu.chainfire.geolog.Debug.java
eu.chainfire.geolog.data.Database.java
eu.chainfire.geolog.data.Exporter.java
eu.chainfire.geolog.data.LogsProvider.java
eu.chainfire.geolog.data.ProfilesProvider.java
eu.chainfire.geolog.service.BackgroundService.java
eu.chainfire.geolog.service.BootCompleteReceiver.java
eu.chainfire.geolog.ui.ExportActivity.java
eu.chainfire.geolog.ui.LogsFragment.java
eu.chainfire.geolog.ui.MainActivity.java
eu.chainfire.geolog.ui.Pref.java
eu.chainfire.geolog.ui.PreferenceListFragment.java
eu.chainfire.geolog.ui.ProfileActivity.java
eu.chainfire.geolog.ui.ProfilesFragment.java
eu.chainfire.geolog.ui.SettingsFragment.java