自作地図アプリは8年前にWindows C#で開発したものをAndroidスマホに移植したものであり、 GPSログは8年間の蓄積があり、途中で何回かファイル形式を少し変更している。
過去のGPSログ表示プログラムの簡単化とパフォーマンス向上のために、全てのログを最新フォーマットに変換しておく。
元のGPSログファイル名には日付情報のみを含むが、過去ログ表示用ログファイルの名前には、日付の他に境界ボックス座標値も含める。
地図アプリ起動後、初めて、過去ログを表示するときに、ファイル名一覧を読み込む。
現在の地図アプリにおける class GPSLog は以下の通りである。
public class GPSLog { final static int E7 = 10000000; static ListlistLogs; // 今日の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; } }