org.openbmap.soapclient.GpxExporter.java Source code

Java tutorial

Introduction

Here is the source code for org.openbmap.soapclient.GpxExporter.java

Source

/*
   Radiobeacon - Openbmap wifi and cell logger
Copyright (C) 2013  wish7
    
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
    
This program 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 Affero General Public License for more details.
    
You should have received a copy of the GNU Affero General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package org.openbmap.soapclient;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;

import org.apache.commons.lang3.StringEscapeUtils;
import org.openbmap.RadioBeacon;
import org.openbmap.db.DatabaseHelper;
import org.openbmap.db.Schema;

import android.content.Context;
import android.database.Cursor;
import android.util.Log;

/**
 * Writes GPX file for session
 * Inspired by Nicolas Guillaumin
 */
// TODO: refactor to AsyncTask
public class GpxExporter {

    private static final String TAG = GpxExporter.class.getSimpleName();

    /**
     * Cursor windows size, to prevent running out of mem on to large cursor
     */
    private static final int CURSOR_SIZE = 1000;

    /**
     * XML header.
     */
    private static final String XML_HEADER = "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n";

    /**
     * GPX opening tag
     */
    private static final String TAG_GPX = "<gpx" + " xmlns=\"http://www.topografix.com/GPX/1/1\""
            + " version=\"1.1\"" + " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""
            + " xsi:schemaLocation=\"http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd \">\n";

    private static final String TAG_GPX_CLOSE = "</gpx>";

    private static final String TRACKPOINT_SQL_QUERY1 = "SELECT " + Schema.COL_LATITUDE + ", "
            + Schema.COL_LONGITUDE + ", " + " " + Schema.COL_ALTITUDE + ", " + Schema.COL_ACCURACY + ", "
            + Schema.COL_TIMESTAMP + ", " + Schema.COL_BEARING + ", " + Schema.COL_SPEED + ", "
            + Schema.COL_SESSION_ID + ", " + Schema.COL_SOURCE + " " + " FROM " + Schema.TBL_POSITIONS + " WHERE "
            + Schema.COL_SESSION_ID + " = ?" + " AND source != '" + RadioBeacon.PROVIDER_USER_DEFINED + "' "
            + " ORDER BY " + Schema.COL_TIMESTAMP + " LIMIT " + CURSOR_SIZE + " OFFSET ?";

    private static final String WAYPOINT_SQL_QUERY = "SELECT " + Schema.COL_LATITUDE + ", " + Schema.COL_LONGITUDE
            + ", " + " " + Schema.COL_ALTITUDE + ", " + Schema.COL_ACCURACY + ", " + Schema.COL_TIMESTAMP + ", "
            + Schema.COL_BEARING + ", " + Schema.COL_SPEED + ", " + Schema.COL_SESSION_ID + ", " + Schema.COL_SOURCE
            + " " + " FROM " + Schema.TBL_POSITIONS + " WHERE " + Schema.COL_SESSION_ID + " = ?" + " AND source = '"
            + RadioBeacon.PROVIDER_USER_DEFINED + "' " + " ORDER BY " + Schema.COL_TIMESTAMP + " LIMIT "
            + CURSOR_SIZE + " OFFSET ?";

    /*
    private static final String WIFI_POINTS_SQL_QUERY = "SELECT " + Schema.COL_LATITUDE + ", " + Schema.COL_LONGITUDE + ", "
     + Schema.COL_ALTITUDE + ", " + Schema.COL_ACCURACY + ", " + Schema.COL_TIMESTAMP + ", \"WIFI \"||" + Schema.COL_SSID + " AS name "
     + " FROM " + Schema.TBL_POSITIONS + " AS p LEFT JOIN " 
     + " (SELECT " + Schema.COL_ID + ", " + Schema.COL_SSID + ", " + Schema.COL_BEGIN_POSITION_ID + " FROM " + Schema.TBL_WIFIS + " )"
     + " AS w ON w." + Schema.COL_BEGIN_POSITION_ID + " = p._id WHERE w._id IS NOT NULL AND p." + Schema.COL_SESSION_ID + " = ?"
     + " ORDER BY " + Schema.COL_TIMESTAMP
     + " LIMIT " + CURSOR_SIZE + " OFFSET ?";*/

    private static final String WIFI_POINTS_SQL_QUERY = "SELECT w.rowid as " + Schema.COL_ID + ", w."
            + Schema.COL_BSSID + ", w." + Schema.COL_SSID + ", " + " MAX(" + Schema.COL_LEVEL + "), w."
            + Schema.COL_TIMESTAMP + ", " + " b." + Schema.COL_LATITUDE + ", b." + Schema.COL_LONGITUDE + ", b."
            + Schema.COL_ALTITUDE + ", b." + Schema.COL_ACCURACY + " FROM " + Schema.TBL_WIFIS
            + " as w JOIN positions as b ON request_pos_id = b._id " + " WHERE w." + Schema.COL_SESSION_ID
            + " = ? GROUP BY w." + Schema.COL_BSSID + " LIMIT " + CURSOR_SIZE + " OFFSET ?";

    private static final String CELL_POINTS_SQL_QUERY = "SELECT " + Schema.COL_LATITUDE + ", "
            + Schema.COL_LONGITUDE + ", " + Schema.COL_ALTITUDE + ", " + Schema.COL_ACCURACY + ", "
            + Schema.COL_TIMESTAMP + ", \"CELL \" ||" + Schema.COL_OPERATORNAME + " ||" + Schema.COL_LOGICAL_CELLID
            + " AS name" + " FROM " + Schema.TBL_POSITIONS + " AS p LEFT JOIN " + " (SELECT " + Schema.COL_ID + ", "
            + Schema.COL_OPERATORNAME + ", " + Schema.COL_LOGICAL_CELLID + ", " + Schema.COL_BEGIN_POSITION_ID
            + " FROM " + Schema.TBL_CELLS + ") " + " AS c ON c." + Schema.COL_BEGIN_POSITION_ID
            + " = p._id WHERE c._id IS NOT NULL AND p." + Schema.COL_SESSION_ID + " = ?" + " ORDER BY "
            + Schema.COL_TIMESTAMP + " LIMIT " + CURSOR_SIZE + " OFFSET ?";

    /**
     * Date format as used internally
     */
    private static final SimpleDateFormat INTERNAL_DATE_FORMAT = new SimpleDateFormat("yyyyMMddHHmmss", Locale.US);

    /**
     * Date format for a gpx point timestamp.
     * ISO 8601 format
     */
    private static final SimpleDateFormat GPX_DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'",
            Locale.US);

    private final int mSession;

    private final Context mContext;

    private DatabaseHelper mDbHelper;

    public GpxExporter(final Context context, final int session) {
        mSession = session;
        mContext = context;
    }

    /**
     * Writes the GPX file
     * @param trackName Name of the GPX track (metadata)
     * @param cTrackPoints Cursor to track points.
     * @param cWayPoints Cursor to way points.
     * @param target Target GPX file
     * @throws IOException 
     */
    public final void doExport(final String trackName, final File target) throws IOException {
        Log.i(TAG, "Finished building gpx file");
        mDbHelper = new DatabaseHelper(mContext);

        final BufferedWriter bw = new BufferedWriter(new FileWriter(target));

        bw.write(XML_HEADER);
        bw.write(TAG_GPX);

        writeWaypoints(mSession, bw);
        writeTrackpoints(mSession, trackName, bw);
        bw.flush();

        writeWifis(bw);
        bw.flush();
        writeCells(bw);
        bw.flush();

        bw.write(TAG_GPX_CLOSE);
        bw.close();

        mDbHelper.close();
        Log.i(TAG, "Finished building gpx file");
    }

    /**
     * Iterates on track points and write them.
     * @param trackName Name of the track (metadata).
     * @param bw Writer to the target file.
     * @param c Cursor to track points.
     * @throws IOException
     */
    private void writeWaypoints(final int session, final BufferedWriter bw) throws IOException {
        Log.i(TAG, "Writing trackpoints");

        Cursor c = mDbHelper.getReadableDatabase().rawQuery(WAYPOINT_SQL_QUERY,
                new String[] { String.valueOf(mSession), String.valueOf(0) });

        final int colLatitude = c.getColumnIndex(Schema.COL_LATITUDE);
        final int colLongitude = c.getColumnIndex(Schema.COL_LONGITUDE);
        final int colAltitude = c.getColumnIndex(Schema.COL_ALTITUDE);
        final int colTimestamp = c.getColumnIndex(Schema.COL_TIMESTAMP);

        long outer = 0;
        while (!c.isAfterLast()) {
            c.moveToFirst();
            //StringBuffer out = new StringBuffer(32 * 1024);
            while (!c.isAfterLast()) {
                bw.write("\t\t\t<wpt lat=\"");
                bw.write(String.valueOf(c.getDouble(colLatitude)));
                bw.write("\" ");
                bw.write("lon=\"");
                bw.write(String.valueOf(c.getDouble(colLongitude)));
                bw.write("\">");
                bw.write("<ele>");
                bw.write(String.valueOf(c.getDouble(colAltitude)));
                bw.write("</ele>");
                bw.write("<time>");
                // time stamp conversion to ISO 8601
                bw.write(getGpxDate(c.getLong(colTimestamp)));
                bw.write("</time>");
                bw.write("</wpt>\n");

                c.moveToNext();
            }
            //bw.write(out.toString());
            //out = null;

            // fetch next CURSOR_SIZE records
            outer += CURSOR_SIZE;
            c.close();
            c = mDbHelper.getReadableDatabase().rawQuery(WAYPOINT_SQL_QUERY,
                    new String[] { String.valueOf(mSession), String.valueOf(outer) });
        }
        c.close();
        System.gc();
    }

    /**
     * Iterates on track points and write them.
     * @param trackName Name of the track (metadata).
     * @param bw Writer to the target file.
     * @param c Cursor to track points.
     * @throws IOException
     */
    private void writeTrackpoints(final int session, final String trackName, final BufferedWriter bw)
            throws IOException {
        Log.i(TAG, "Writing trackpoints");

        Cursor c = mDbHelper.getReadableDatabase().rawQuery(TRACKPOINT_SQL_QUERY1,
                new String[] { String.valueOf(mSession), String.valueOf(0) });

        final int colLatitude = c.getColumnIndex(Schema.COL_LATITUDE);
        final int colLongitude = c.getColumnIndex(Schema.COL_LONGITUDE);
        final int colAltitude = c.getColumnIndex(Schema.COL_ALTITUDE);
        final int colTimestamp = c.getColumnIndex(Schema.COL_TIMESTAMP);

        bw.write("\t<trk>");
        bw.write("\t\t<name>");
        bw.write(trackName);
        bw.write("</name>\n");
        bw.write("\t\t<trkseg>\n");

        long outer = 0;
        while (!c.isAfterLast()) {
            c.moveToFirst();
            //StringBuffer out = new StringBuffer(32 * 1024);
            while (!c.isAfterLast()) {
                bw.write("\t\t\t<trkpt lat=\"");
                bw.write(String.valueOf(c.getDouble(colLatitude)));
                bw.write("\" ");
                bw.write("lon=\"");
                bw.write(String.valueOf(c.getDouble(colLongitude)));
                bw.write("\">");
                bw.write("<ele>");
                bw.write(String.valueOf(c.getDouble(colAltitude)));
                bw.write("</ele>");
                bw.write("<time>");
                // time stamp conversion to ISO 8601
                bw.write(getGpxDate(c.getLong(colTimestamp)));
                bw.write("</time>");
                bw.write("</trkpt>\n");

                c.moveToNext();
            }
            //bw.write(out.toString());
            //out = null;

            // fetch next CURSOR_SIZE records
            outer += CURSOR_SIZE;
            c.close();
            c = mDbHelper.getReadableDatabase().rawQuery(TRACKPOINT_SQL_QUERY1,
                    new String[] { String.valueOf(mSession), String.valueOf(outer) });
        }
        c.close();

        bw.write("\t\t</trkseg>\n");
        bw.write("\t</trk>\n");
        System.gc();
    }

    /**
     * Iterates on way points and write them.
     * @param bw Writer to the target file.
     * @param c Cursor to way points.
     * @throws IOException
     */
    private void writeWifis(final BufferedWriter bw) throws IOException {
        Log.i(TAG, "Writing wifi waypoints");

        Cursor c = mDbHelper.getReadableDatabase().rawQuery(WIFI_POINTS_SQL_QUERY,
                new String[] { String.valueOf(mSession), String.valueOf(0) });
        //Log.i(TAG, WIFI_POINTS_SQL_QUERY);
        final int colLatitude = c.getColumnIndex(Schema.COL_LATITUDE);
        final int colLongitude = c.getColumnIndex(Schema.COL_LONGITUDE);
        final int colAltitude = c.getColumnIndex(Schema.COL_ALTITUDE);
        final int colTimestamp = c.getColumnIndex(Schema.COL_TIMESTAMP);
        final int colSsid = c.getColumnIndex(Schema.COL_SSID);

        long outer = 0;
        while (!c.isAfterLast()) {
            c.moveToFirst();
            //StringBuffer out = new StringBuffer();
            while (!c.isAfterLast()) {
                bw.write("\t<wpt lat=\"");
                bw.write(String.valueOf(c.getDouble(colLatitude)));
                bw.write("\" ");
                bw.write("lon=\"");
                bw.write(String.valueOf(c.getDouble(colLongitude)));
                bw.write("\">\n");
                bw.write("\t\t<ele>");
                bw.write(String.valueOf(c.getDouble(colAltitude)));
                bw.write("</ele>\n");
                bw.write("\t\t<time>");
                // time stamp conversion to ISO 8601
                bw.write(getGpxDate(c.getLong(colTimestamp)));
                bw.write("</time>\n");
                bw.write("\t\t<name>");
                bw.write(StringEscapeUtils.escapeXml(c.getString(colSsid)));
                bw.write("</name>\n");
                bw.write("\t</wpt>\n");

                c.moveToNext();
            }
            //bw.write(out.toString());
            //out = null;
            // fetch next CURSOR_SIZE records
            outer += CURSOR_SIZE;
            c.close();
            c = mDbHelper.getReadableDatabase().rawQuery(WIFI_POINTS_SQL_QUERY,
                    new String[] { String.valueOf(mSession), String.valueOf(outer) });
        }
        c.close();
        System.gc();
    }

    /**
     * Iterates on way points and write them.
     * @param bw Writer to the target file.
     * @param c Cursor to way points.
     * @throws IOException
     */
    private void writeCells(final BufferedWriter bw) throws IOException {
        Log.i(TAG, "Writing cell waypoints");
        Cursor c = mDbHelper.getReadableDatabase().rawQuery(CELL_POINTS_SQL_QUERY,
                new String[] { String.valueOf(mSession), String.valueOf(0) });

        final int colLatitude = c.getColumnIndex(Schema.COL_LATITUDE);
        final int colLongitude = c.getColumnIndex(Schema.COL_LONGITUDE);
        final int colAltitude = c.getColumnIndex(Schema.COL_ALTITUDE);
        final int colTimestamp = c.getColumnIndex(Schema.COL_TIMESTAMP);
        final int colName = c.getColumnIndex("name");

        long outer = 0;
        while (!c.isAfterLast()) {
            c.moveToFirst();
            //StringBuffer out = new StringBuffer();
            while (!c.isAfterLast()) {
                bw.write("\t<wpt lat=\"");
                bw.write(String.valueOf(c.getDouble(colLatitude)));
                bw.write("\" ");
                bw.write("lon=\"");
                bw.write(String.valueOf(c.getDouble(colLongitude)));
                bw.write("\">\n");
                bw.write("\t\t<ele>");
                bw.write(String.valueOf(c.getDouble(colAltitude)));
                bw.write("</ele>\n");
                bw.write("\t\t<time>");
                // time stamp conversion to ISO 8601
                bw.write(getGpxDate(c.getLong(colTimestamp)));
                bw.write("</time>\n");
                bw.write("\t\t<name>");
                bw.write(StringEscapeUtils.escapeXml(c.getString(colName)));
                bw.write("</name>\n");
                bw.write("\t</wpt>\n");

                c.moveToNext();
            }

            //bw.write(out.toString());
            //out = null;
            // fetch next CURSOR_SIZE records
            outer += CURSOR_SIZE;
            c.close();
            c = mDbHelper.getReadableDatabase().rawQuery(CELL_POINTS_SQL_QUERY,
                    new String[] { String.valueOf(mSession), String.valueOf(outer) });
        }
        c.close();
        System.gc();
    }

    /**
     * Converts from openbmap date format (YYYYMMDDHHMMSS) to gpx date format (ISO 8601)
     * @param raw Openbmap date
     * @return ISO 8601 date
     */
    private static String getGpxDate(final long raw) {
        try {
            // for gpx files we need data in ISO 8601 format (e.g. 2011-12-31T23:59:59Z)
            // as opposed to openbmap database format YYYYMMDDHHMMSS
            final Date converted = INTERNAL_DATE_FORMAT.parse(String.valueOf(raw));
            GPX_DATE_FORMAT.setTimeZone(TimeZone.getTimeZone("UTC"));
            return (GPX_DATE_FORMAT.format(converted));
        } catch (final ParseException e) {
            // should never happen
            Log.e(TAG, "Error converting gpx date. Source " + raw);
            return "0000-00-00T00:00:00Z";
        }
    }
}