自作地図アプリは8年前にWindows C#で開発したものをAndroidスマホに移植したものであり、 GPSログは8年間の蓄積があり、途中で何回かファイル形式を少し変更している。
過去のGPSログ表示プログラムの簡単化とパフォーマンス向上のために、全てのログを最新フォーマットに変換しておく。
元のGPSログファイル名には日付情報のみを含むが、過去ログ表示用ログファイルの名前には、日付の他に境界ボックス座標値も含める。
地図アプリ起動後、初めて、過去ログを表示するときに、ファイル名一覧を読み込む。
現在の地図アプリにおける class GPSLog は以下の通りである。
public class GPSLog {
final static int E7 = 10000000;
static List listLogs; // 今日のGPSログ
LocalDateTime ldt;
int ilon, ilat, ialt;
float speed, accuracy = -1;
public GPSLog(String loc) {
String[] v = loc.split(",");
ilon = (int)Math.round(Double.parseDouble(v[1]) * E7);
ilat = (int)Math.round(Double.parseDouble(v[2]) * E7);
ialt = (int)Math.round(Double.parseDouble(v[3]));
speed = Float.parseFloat(v[4]);
ldt = LocalDateTime.now(); // 2022.5.11
}
// 時:分:秒, lon, lat, alt, speed, accuracy,
//19:52:17,139.5044180,35.5452761,96.4,0.0,9.9
public GPSLog(int date, String line) {
String[] v = line.split("[,:]");
int year = date / 10000;
int month = (date % 10000) / 100;
int day = date % 100;
ldt = LocalDateTime.of(year, month, day, // 2021/1/1 10:20:30.000
Integer.parseInt(v[0]), Integer.parseInt(v[1]), Integer.parseInt(v[2]));
ilon = (int)Math.round(Double.parseDouble(v[3]) * E7);
ilat = (int)Math.round(Double.parseDouble(v[4]) * E7);
ialt = (int)Math.round(Double.parseDouble(v[5]));
if (v.length >= 8) {
speed = Float.parseFloat(v[6]);
accuracy = Float.parseFloat(v[7]);
}
}
public static void LoadGPSLog() {
LocalDateTime now = LocalDateTime.now();
int date = now.getYear()*10000 + now.getMonthValue()*100 + now.getDayOfMonth();
listLogs = LoadGPSLog(date);
}
// ファイル読み込み
public static List LoadGPSLog(int date) {
List listLogs = new ArrayList<>();
String file = String.format(Locale.JAPAN, "%sgpslogs/%d.csv", Map.DIR, date);
int lastLon = 0, lastLat = 0;
//Log.d("GPSLog", file);
if (new File(file).exists()) {
try {
Path path = Paths.get(file);
List lines = Files.readAllLines(path);
for (String line : lines) {
GPSLog log = new GPSLog(date, line);
if (log.ilon != lastLon || log.ilat != lastLat) {
lastLon = log.ilon;
lastLat = log.ilat;
listLogs.add(log);
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
return listLogs;
}
}
初期のデータ(2015.3.14~)は以下の通りである。
448098102,139.504442,35.545372,94.106010
先頭の 448098102 は 2001年1月1日からの通算秒である。Speed および Accuracy はない。
すぐに(2015.4.1~2021.11.30)、時刻のフォーマットを現在の形に変更した。ファイル名は p20150401.csv の形である。
17:45:48,139.504262,35.545331,91.9
したがって、最初の半月分は無視してもよいだろう。
2022.1.27 からスマホの地図アプリで現在のフォーマットとした。2021.12.1 から 2022.1.26 の2か月弱はGPSログはとっていない。 ファイル名は 20220127.csv の形とした。
Speed は徒歩か車かの区別に利用している。単位は「m/秒」である。Windows時代にはこの値を取得、保存していなかった。 この値がある方が表示プログラムの負担が減るので、補うことにする。
Windowsのときは極座標で直接、徒歩か車かを判定していたが、今回は、次のプログラムで、2点間の極座標から2点間の距離(単位:m)を求めて、 その値を2点間の時刻差(単位:秒)で割って、速度(m/秒単位)とする。
double deg2rad(double deg) { return (deg/180.0)*Math.PI; }
double calculateDistance(double lonA, double latA, double lonB, double latB) {
double latAvg = deg2rad(latA + ((latB - latA) / 2));
double latDifference = deg2rad(latA - latB);
double lonDifference = deg2rad(lonA - lonB);
double curRadiusTemp = 1 - 0.00669438 * Math.pow(Math.sin(latAvg), 2);
double meridianCurvatureRadius = 6335439.327/Math.sqrt(Math.pow(curRadiusTemp,3));
double primeVerticalCircleCurvatureRadius = 6378137 / Math.sqrt(curRadiusTemp);
double distance2 = Math.pow(meridianCurvatureRadius*latDifference,2) +
Math.pow(primeVerticalCircleCurvatureRadius*Math.cos(latAvg)*lonDifference,2);
return Math.sqrt(distance2);
}
class GPSLogIndex を追加した。class GPSLog を少し修正した。
class GPSLogIndex {
final static double E7 = 10000000.0;
static GPSLogIndex[] indexes; // 地図アプリ起動時に読込む
int date; // ex. 20231009
int minLon, minLat, maxLon, maxLat;
Rect rect;
String filename;
public GPSLogIndex (int date, int minLon, int minLat, int maxLon, int maxLat) {
this.date = date;
this.minLon = minLon;
this.minLat = minLat;
this.maxLon = maxLon;
this.maxLat = maxLat;
rect = new Rect(minLon, minLat, maxLon, maxLat);
}
public GPSLogIndex (String filename) {
String[] v = filename.split("[_\\.]");
this.filename = filename;
this.date = Integer.parseInt(v[0]);
this.minLon = Integer.parseInt(v[1]);
this.minLat = Integer.parseInt(v[2]);
this.maxLon = Integer.parseInt(v[3]);
this.maxLat = Integer.parseInt(v[4]);
rect = new Rect(minLon, minLat, maxLon, maxLat);
}
static void loadGPSLogIndex() {
File directory = new File(Map.DIR + "p_gpslogs");
String[] files = directory.list();
Arrays.sort(files); // 日付が古い順
System.out.printf("GPSログ・ファイル数: %d\n", files.length);
indexes = new GPSLogIndex[files.length];
for (int n = 0; n < indexes.length; n++) {
String filename = files[indexes.length-1-n]; // 新しい順
//String[] v = filename.split("[_\\.]");
indexes[n] = new GPSLogIndex(filename);
}
}
}
class GPSLog {
final static int E7 = 10000000;
static List<GPSLog> listLogs; // 今日のGPSログ
LocalDateTime ldt;
int ilon, ilat, ialt;
float speed, accuracy = -1;
public GPSLog(String loc) {
String[] v = loc.split(",");
ilon = (int)Math.round(Double.parseDouble(v[1]) * E7);
ilat = (int)Math.round(Double.parseDouble(v[2]) * E7);
ialt = (int)Math.round(Double.parseDouble(v[3]));
speed = Float.parseFloat(v[4]);
ldt = LocalDateTime.now(); // 2022.5.11
}
//19:52:17,139.5944180,35.5952761,96.4,0.0,9.9
public GPSLog(int date, String line) {
String[] v = line.split("[,:]");
int year = date / 10000;
int month = (date % 10000) / 100;
int day = date % 100;
ldt = LocalDateTime.of(year, month, day, // 2021/1/1 10:20:30.000
Integer.parseInt(v[0]), Integer.parseInt(v[1]), Integer.parseInt(v[2]));
ilon = (int)Math.round(Double.parseDouble(v[3]) * E7);
ilat = (int)Math.round(Double.parseDouble(v[4]) * E7);
ialt = (int)Math.round(Double.parseDouble(v[5]));
if (v.length >= 8) {
speed = Float.parseFloat(v[6]);
accuracy = Float.parseFloat(v[7]);
}
}
// その日のファイル読み込み
public static void LoadGPSLog() {
LocalDateTime now = LocalDateTime.now();
int date = now.getYear()*10000 + now.getMonthValue()*100 + now.getDayOfMonth();
listLogs = LoadGPSLog(date);
}
// 変換ファイル読み込み
public static List<GPSLog> LoadGPSLog(String filename) {
List<GPSLog> listLogs = new ArrayList<>();
String file = Map.DIR + "p_gpslogs/" + filename;
int lastLon = 0, lastLat = 0;
Log.d("GPSLog", file);
if (new File(file).exists()) {
try {
Path path = Paths.get(file);
List<String> lines = Files.readAllLines(path);
for (String line : lines) {
GPSLog log = new GPSLog(line);
if (log.ilon != lastLon || log.ilat != lastLat) {
lastLon = log.ilon;
lastLat = log.ilat;
listLogs.add(log);
}
}
} catch (IOException e) {
e.printStackTrace();
}
} else {
Log.d("GPSLog no", file);
}
return listLogs;
}
// 元ファイルの読み込み
public static List<GPSLog> LoadGPSLog(int date) {
List<GPSLog> listLogs = new ArrayList<>();
String file = String.format(Locale.JAPAN, "%sgpslogs/%d.csv", Map.DIR, date);
int lastLon = 0, lastLat = 0;
Log.d("GPSLog", file);
if (new File(file).exists()) {
try {
Path path = Paths.get(file);
List<String> lines = Files.readAllLines(path);
for (String line : lines) {
GPSLog log = new GPSLog(date, line);
if (log.ilon != lastLon || log.ilat != lastLat) {
lastLon = log.ilon;
lastLat = log.ilat;
listLogs.add(log);
}
}
} catch (IOException e) {
e.printStackTrace();
}
} else {
Log.d("GPSLog no", file);
}
return listLogs;
}
}
とりあえず以下のプログラムでGPSLog履歴が表示できるようになった。 この後、少しずつ改良したい。
void drawHistories(Canvas canvas, int days) {
int k = 0;
Rect rectScreen = getRect();
System.out.printf("%d %d %d %d\n",
rectScreen.left, rectScreen.top, rectScreen.right, rectScreen.bottom);
for (int n = 0; n < GPSLogIndex.indexes.length; n++) {
GPSLogIndex idx = GPSLogIndex.indexes[n]; // 新しい順
if (idx.rect.intersect(rectScreen)) {
if (k++ >= days) break;
System.out.printf("%d/%d %s\n", k, n, idx.filename);
drawHistory(canvas, idx.filename, 0xff000000);
}
}
}
void drawHistory(Canvas canvas, String filename, int color) {
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(1.5f*Map.Scale);
List<GPSLog> logs = GPSLog.LoadGPSLog(filename);
if (logs.size() == 0) return;
float xB = getX(logs.get(0).ilon);
float yB = getY(logs.get(0).ilat);
LocalDateTime tB = logs.get(0).ldt;
for (int n = 1; n < logs.size(); n++) {
GPSLog log = logs.get(n);
float xE = getX(log.ilon);
float yE = getY(log.ilat);
LocalDateTime tE = log.ldt;
if (ChronoUnit.MINUTES.between(tB,tE) < 5) { // 2023.9.20
paint.setColor(color);
//System.out.printf("(%f %f) (%f %f)\n", xB, yB, xE, yE);
canvas.drawLine(xB, yB, xE, yE, paint);
}
xB = xE;
yB = yE;
tB = tE;
}
}