org.openbmap.soapclient.GpxSerializer.java Source code

Java tutorial

Introduction

Here is the source code for org.openbmap.soapclient.GpxSerializer.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 android.content.Context;
import android.database.Cursor;
import android.support.annotation.NonNull;
import android.util.Log;

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

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;

/**
 * Writes GPX file for session
 * Inspired by Nicolas Guillaumin
 */
public class GpxSerializer {

    private static final String TAG = GpxSerializer.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
     */
    //@formatter:off
    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 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 ?";
    //@formatter:on
    /**
     * 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);

    /**
     * Levels of detail for GPX export
     */
    private static final int VERBOSITY_TRACK_AND_WAYPOINTS = 1;
    private static final int VERBOSITY_WAYPOINTS_ONLY = 2;
    private static final int VERBOSITY_ALL = 3;

    private final int mSession;

    private final Context mContext;

    private DatabaseHelper mDbHelper;

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

    /**
     * Writes the GPX file
     *
     * @param trackName
     *         Name of the GPX track (metadata)
     * @param target
     *         Target GPX file
     * @param verbosity
     *         GPX verbosity (see constants above)
     */
    public final void doExport(final String trackName, final File target, int verbosity) throws IOException {
        Log.i(TAG, "Exporting gpx file" + target.getAbsolutePath());
        mDbHelper = new DatabaseHelper(mContext.getApplicationContext());

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

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

        if (verbosity == VERBOSITY_TRACK_AND_WAYPOINTS || verbosity == VERBOSITY_WAYPOINTS_ONLY
                || verbosity == VERBOSITY_ALL) {
            writeWaypoints(bw);
        }

        if (verbosity == VERBOSITY_TRACK_AND_WAYPOINTS || verbosity == VERBOSITY_ALL) {
            writeTrackpoints(trackName, bw);
        }
        bw.flush();

        if (verbosity == VERBOSITY_ALL) {
            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 bw
     *         Writer to the target file.
     */
    private void writeWaypoints(final BufferedWriter bw) throws IOException {
        Log.i(TAG, "Writing trackpoints");

        //@formatter:off
        Cursor c = mDbHelper.getReadableDatabase().rawQuery(WAYPOINT_SQL_QUERY,
                new String[] { String.valueOf(mSession), String.valueOf(0) });
        //@formatter:on

        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();
            while (!c.isAfterLast()) {
                StringBuilder stringBuilder = new StringBuilder();
                stringBuilder.append("<wpt lat=\"");
                stringBuilder.append(String.valueOf(c.getDouble(colLatitude)));
                stringBuilder.append("\" ");
                stringBuilder.append("lon=\"");
                stringBuilder.append(String.valueOf(c.getDouble(colLongitude)));
                stringBuilder.append("\">");
                stringBuilder.append("<ele>");
                stringBuilder.append(String.valueOf(c.getDouble(colAltitude)));
                stringBuilder.append("</ele>");
                stringBuilder.append("<time>");
                // time stamp conversion to ISO 8601
                stringBuilder.append(getGpxDate(c.getLong(colTimestamp)));
                stringBuilder.append("</time>");
                stringBuilder.append("</wpt>");

                bw.write(stringBuilder.toString());
                bw.flush(); //in case of exception we don't lose data
                c.moveToNext();
            }

            // fetch next CURSOR_SIZE records
            outer += CURSOR_SIZE;
            c.close();

            //@formatter:off
            c = mDbHelper.getReadableDatabase().rawQuery(WAYPOINT_SQL_QUERY,
                    new String[] { String.valueOf(mSession), String.valueOf(outer) });
            //@formatter:on
        }

        c.close();
    }

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

        //@formatter:off
        Cursor c = mDbHelper.getReadableDatabase().rawQuery(TRACKPOINT_SQL_QUERY1,
                new String[] { String.valueOf(mSession), String.valueOf(0) });
        //@formatter:on

        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("<trk>");
        bw.write("<name>");
        bw.write(trackName);
        bw.write("</name>");
        bw.write("<trkseg>");

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

                bw.write(out.toString());
                bw.flush();
                c.moveToNext();
            }

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

        bw.write("</trkseg>");
        bw.write("</trk>");
        bw.flush();
    }

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

        //@formatter:off
        Cursor c = mDbHelper.getReadableDatabase().rawQuery(WIFI_POINTS_SQL_QUERY,
                new String[] { String.valueOf(mSession), String.valueOf(0) });
        //@formatter:on

        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();
            while (!c.isAfterLast()) {
                StringBuffer out = new StringBuffer();
                out.append("<wpt lat=\"");
                out.append(String.valueOf(c.getDouble(colLatitude)));
                out.append("\" ");
                out.append("lon=\"");
                out.append(String.valueOf(c.getDouble(colLongitude)));
                out.append("\">");
                out.append("<ele>");
                out.append(String.valueOf(c.getDouble(colAltitude)));
                out.append("</ele>\n");
                out.append("<time>");
                // time stamp conversion to ISO 8601
                out.append(getGpxDate(c.getLong(colTimestamp)));
                out.append("</time>");
                out.append("<name>");
                out.append(StringEscapeUtils.escapeXml10(c.getString(colSsid)));
                out.append("</name>");
                out.append("</wpt>");
                bw.write(out.toString());
                bw.flush();
                c.moveToNext();
            }
            // fetch next CURSOR_SIZE records
            outer += CURSOR_SIZE;
            c.close();
            //@formatter:off
            c = mDbHelper.getReadableDatabase().rawQuery(WIFI_POINTS_SQL_QUERY,
                    new String[] { String.valueOf(mSession), String.valueOf(outer) });
            //@formatter:on
        }
        c.close();
    }

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

        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();
            while (!c.isAfterLast()) {
                StringBuffer out = new StringBuffer();
                out.append("<wpt lat=\"");
                out.append(String.valueOf(c.getDouble(colLatitude)));
                out.append("\" ");
                out.append("lon=\"");
                out.append(String.valueOf(c.getDouble(colLongitude)));
                out.append("\">");
                out.append("<ele>");
                out.append(String.valueOf(c.getDouble(colAltitude)));
                out.append("</ele>");
                out.append("<time>");
                // time stamp conversion to ISO 8601
                out.append(getGpxDate(c.getLong(colTimestamp)));
                out.append("</time>");
                out.append("<name>");
                out.append(StringEscapeUtils.escapeXml10(c.getString(colName)));
                out.append("</name>");
                out.append("</wpt>");

                bw.write(out.toString());
                bw.flush();
                c.moveToNext();
            }

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

    /**
     * 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";
        }
    }

    @NonNull
    public static final String suggestGpxFilename(int id) {
        final SimpleDateFormat date = new SimpleDateFormat("yyyyMMddhhmmss", Locale.US);
        return date.format(new Date(System.currentTimeMillis())) + "(" + String.valueOf(id) + ")" + ".gpx";
    }
}