/*
Copyright 2012 Nik Cain nik@showmehills.com
This file is part of ShowMeHills.
ShowMeHills is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
ShowMeHills 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with ShowMeHills. If not, see <http://www.gnu.org/licenses/>.
*/
package com.showmehills;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import android.content.Context;
import android.content.SharedPreferences;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteException;
import android.database.sqlite.SQLiteOpenHelper;
import android.location.Location;
import android.preference.PreferenceManager;
import android.util.Log;
public class HillDatabase extends SQLiteOpenHelper{
private static String DB_PATH;// = "/data/data/com.showmehills.showmehillsuk/databases/";
private static String DB_NAME;
private static int mDatabaseVersion = 9;
private SQLiteDatabase myDataBase;
private final Context myContext;
private boolean mDbCopied = false;
public ArrayList<Hills> localhills = new ArrayList<Hills>();
public HillDatabase(Context context, String dbname, String dbpath) {
super(context, dbname, null, 1);
DB_NAME = dbname;
DB_PATH = dbpath;
this.myContext = context;
}
public void createDataBase(){
// made some changes in the database, but need to update it in existing installs!
// so need to add a version number
// for now just update db every time
boolean dbExist = checkDataBase();
if(dbExist){
//do nothing - database already exist
}else{
this.getReadableDatabase();
copyDataBase();
}
}
public boolean checkDataBase(){
if (!mDbCopied) return false;
if (myDataBase != null)
{
// already ok
return true;
}
try{
String myPath = DB_PATH + DB_NAME;
try {
myDataBase = SQLiteDatabase.openDatabase(myPath, null, SQLiteDatabase.OPEN_READONLY);
}
catch(Exception e){
return false;
}
if (myDataBase == null)
{
return false;
}
String qu = "select ver from dbversions limit 1";
Cursor cursor = getReadableDatabase().rawQuery( qu, null);
if(cursor.moveToFirst()) {
if (cursor.getInt(0) != mDatabaseVersion)
{
Log.d("showmehills", "Old database ("+cursor.getInt(0)+"). Updating!");
myDataBase.close();
myDataBase = null;
if (myContext.deleteDatabase(DB_NAME))
{
Log.d("showmehills", "Deleted old database " + myContext.getDatabasePath(DB_NAME));
}
else
{
Log.d("showmehills", "Failed to delete old database!");
}
return false;
}
}
}catch(SQLiteException e){
//database does't exist yet.
e.printStackTrace();
}
return myDataBase != null ? true : false;
}
private void copyDataBase() {
Log.d("showmehills", "Attempting to copy database " + DB_NAME + " from assets to " + DB_PATH + DB_NAME);
InputStream myInput;
try {
myInput = myContext.getAssets().open(DB_NAME);
} catch (IOException e) {
e.printStackTrace();
return;
}
String outFileName = DB_PATH + DB_NAME;
OutputStream myOutput;
long bytesCopied = 0;
try {
myOutput = new FileOutputStream(outFileName);
} catch (FileNotFoundException e) {
e.printStackTrace();
try {
myInput.close();
} catch (IOException e1) {
e1.printStackTrace();
}
return;
}
byte[] buffer = new byte[1024];
int length;
try {
while ((length = myInput.read(buffer))>0){
myOutput.write(buffer, 0, length);
bytesCopied += length;
}
} catch (IOException e) {
e.printStackTrace();
try {
myInput.close();
myOutput.close();
} catch (IOException e1) {
e1.printStackTrace();
}
return;
}
try {
myOutput.flush();
myOutput.close();
myInput.close();
} catch (IOException e) {
e.printStackTrace();
}
mDbCopied = true;
Log.d("showmehills", "Database copied successfully (" + bytesCopied + " bytes), attempting to check database again...");
// should be created, so now open
checkDataBase();
}
@Override
public synchronized void close() {
if(myDataBase != null)
myDataBase.close();
myDataBase = null;
super.close();
}
@Override
public void onCreate(SQLiteDatabase db) {}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {}
public void SetDirections(Location curLocation)
{
if (curLocation == null) return;
if (myDataBase == null)
{
createDataBase();
if (myDataBase == null) return;
}
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(myContext);
String md = prefs.getString("distance", "25");
if (md == "") md = "25";
Float maxdistance = Float.parseFloat(md);
md = prefs.getString("mindistance", "0");
if (md == "") md = "0";
Float mindistance = Float.parseFloat(md);
localhills.clear();
double curLatitude = curLocation.getLatitude();
double curLongitude = curLocation.getLongitude();
// use a rule of thumb for distance between lines of lat & long
// 1 line of latitude = 111km
// 1 line of longitude = sin(latitude)* 111km.
String qu = "select * from mountains where latitude between " +
(curLatitude - (maxdistance/111.0 )) + " and " + (curLatitude + (maxdistance/111.0 ))
+ " and longitude between " +
(curLongitude - (maxdistance/(111.0 * Math.sin(curLatitude * Math.PI / 180)))) + " and " +
(curLongitude + (maxdistance/(111.0 * Math.sin(curLatitude * Math.PI / 180))));
Cursor cursor;
try {
cursor = getReadableDatabase().rawQuery( qu, null);
}
catch(SQLiteException e){
return;
}
if (cursor == null) return;
int tooNear = 0, tooFar = 0;
if(cursor.moveToFirst()) {
double dLat, dLon, lat1, lat2;
double x, y, brng, a, c, dheight;
Hills h;
do {
try {
h = new Hills(
cursor.getInt(cursor.getColumnIndex("_id")),
cursor.getString(cursor.getColumnIndex("name")),
cursor.getDouble(cursor.getColumnIndex("longitude")),
cursor.getDouble(cursor.getColumnIndex("latitude")),
cursor.getDouble(cursor.getColumnIndex("height")));
dLat = Math.toRadians(h.latitude - curLatitude);
dLon = Math.toRadians(h.longitude - curLongitude);
lat1 = Math.toRadians(curLatitude);
lat2 = Math.toRadians(h.latitude);
// direction calculation
y = Math.sin(dLon) * Math.cos(lat2);
x = Math.cos(lat1)*Math.sin(lat2) -
Math.sin(lat1)*Math.cos(lat2)*Math.cos(dLon);
brng = Math.atan2(y, x) * 180 / Math.PI;
h.direction = (brng<0)?brng+360:brng;
// distance calculation
a = Math.sin(dLat/2) * Math.sin(dLat/2) +
Math.cos(lat2) * Math.cos(lat1) *
Math.sin(dLon/2) * Math.sin(dLon/2);
c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
h.distance = Math.floor(10 * 6371 * c) / 10.0; // Distance in km
// vertical angle
dheight = h.height - curLocation.getAltitude();
h.visualElevation = Math.atan2(dheight, h.distance*1000);
if (h.distance > maxdistance)
{
tooFar++;
}
else if (h.distance < mindistance)
{
tooNear++;
}
else
{
//Log.d("showmehills", "Adding " + h.hillname + "@"+h.longitude+","+h.latitude);
localhills.add(h);
}
} catch(Exception e)
{
Log.e("showmehills", "bad database read: " + e.getMessage());
}
} while (cursor.moveToNext());
}
Log.d("showmehills", "Added " + localhills.size() + " markers; skipped " + tooNear + " too near, " + tooFar + " too far.");
/*
* for testing:
localhills.add(new Hills(0,"London Eye", -0.119700, 51.5033, 135));
localhills.add(new Hills(0,"Shard", -0.086667, 51.504444, 308));
localhills.add(new Hills(0,"1 Canada Sq", -0.019611, 51.505, 240));
localhills.add(new Hills(0,"BT Tower", -0.138900, 51.5215, 191));
localhills.add(new Hills(0,"Gherkin", -0.080278, 51.514444, 180));
*/
Collections.sort(localhills, new Comparator<Object>(){
public int compare(Object o1, Object o2) {
Hills p1 = (Hills) o1;
Hills p2 = (Hills) o2;
if (p1.distance==p2.distance) return 0;
if (p1.distance < p2.distance) return -1;
return 1;
}
});
}
}