自作地図アプリのバイナリレコードファイルはシンプルな4バイト境界なデータ形式としている。 そのため、日本地図全体では合計 4.5GBほどの大きさとなる。
ブロックに分割しているため、個々のファイルは最大で 50MB 程度であり、 メモリ使用量としては問題はなく、パフォーマンス上、シンプルなデータ形式が望ましい。
しかし、ファイルサイズが大きければ読み込み時間は大きくなる。 ファイルは zip形式として、読み込み処理で4バイト境界のシンプルなデータに展開する方が 総合的には速くなるであろう。
地図用バイナリレコードファイルはパソコンで作成しているため、パソコン側で zip圧縮して、 FTPでスマホに転送する。 現在は Windows のエクスプローラーで スマホに FTP転送しているが、 zip圧縮とFTP転送をプログラムで実行する方が操作性が向上する。
ただし、ZIP圧縮よりもFTP転送プログラムの方が大がかりになるようであれば、 FTP転送はこれまで通りの手作業とする。
パソコンからスマホへの転送だけを考えれば、分割したファイルは zip で一まとめにした方がよいが、 スマホでの読み込みはパフォーマンス重視のため、多数のファイルを一まとめにした zip ファイルからの 読み込みではなく、個々のファイルを個別にzip圧縮したファイルからの読み込みとなる。
一まとめにする方法は記事[1]にあるので、ここでは省略する。
対象のファイル群は階層ディレクトリ下にある。この2階層をスキャンするために、 Files.walk(dirpath, 2) を使う。
以下のプログラムでバイナリレコードファイルを圧縮してみると、 試した例では平均的に半分以下のサイズになった。
一旦、バイナリファイルを出力してから圧縮するのではなく、 ファイル分割プログラムがファイルを出力する時点で zip圧縮して出力する方がよい。 しかし、分割ファイル数が多いため、同時オープンファイル数を抑えるために、時々クローズさせ、 追記モードで書き込みを行っているため、分割プログラムでダイレクトに zip出力するのは簡単ではない。
static void encodeFiles(String dirSrc) {
Path dirpath = Paths.get(dirSrc);
try (Stream<Path> stream = Files.walk(dirpath, 2)) {
stream.forEach(p -> encode(p.toString()));
} catch (IOException e) {
e.printStackTrace();
}
}
static void encode(String strpath) {
if (!strpath.endsWith(".dat")) {
return;
}
byte[] buf = new byte[1024];
String outPath = strpath.replace(".dat", ".zip");
ZipOutputStream zos = null;
try {
zos = new ZipOutputStream(new FileOutputStream(outPath));
} catch (FileNotFoundException e) {
e.printStackTrace();
}
try {
Path path = Paths.get(strpath);
String filename = path.getFileName().toString();
InputStream is = new FileInputStream(strpath);
ZipEntry ze = new ZipEntry(filename);
zos.putNextEntry(ze);
int len = 0;
while ((len = is.read(buf)) != -1) {
zos.write(buf, 0, len);
}
is.close();
zos.closeEntry();
zos.close();
Files.delete(path);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
地図アプリではファイルに展開する必要はなく、直接 int配列に読み込めばよい。
[現在のプログラム]
static int[] readAllInts(String file) throws IOException {
File f = new File(file);
int file_length = (int) f.length();
int[] buffer = new int[file_length / 4];
FileInputStream is = new FileInputStream(file);
for (int offset = 0; offset < file_length; ) {
int nbytes = is.read(ba);
IntBuffer intBuf = ByteBuffer.wrap(ba).order(ByteOrder.BIG_ENDIAN).asIntBuffer();
intBuf.get(buffer, offset / 4, nbytes / 4);
offset += nbytes;
}
is.close();
return buffer;
}
[ZIPファイルを読み込むプログラム]
static int[] readZipAllInts(String file) throws IOException {
ZipInputStream in = new ZipInputStream(new FileInputStream(file));
ZipEntry zipEntry = in.getNextEntry();
int file_length = (int)zipEntry.getSize();
int[] buffer = new int[file_length / 4];
for (int offset = 0; offset < file_length; ) {
int nbytes = in.read(ba);
IntBuffer intBuf = ByteBuffer.wrap(ba).order(ByteOrder.BIG_ENDIAN).asIntBuffer();
intBuf.get(buffer, offset / 4, nbytes / 4);
offset += nbytes;
}
in.closeEntry();
in.close();
return buffer;
}
static int[] readZipAllInts(String file) throws IOException {
ZipInputStream in = new ZipInputStream(new FileInputStream(file));
ZipEntry zipEntry = in.getNextEntry();
int file_length = (int)zipEntry.getSize();
int[] buffer = new int[file_length / 4];
for (int offset = 0; offset < file_length; ) {
int nbytes = in.read(ba);
IntBuffer intBuf = ByteBuffer.wrap(ba).order(ByteOrder.BIG_ENDIAN).asIntBuffer();
intBuf.get(buffer, offset / 4, nbytes / 4);
offset += nbytes;
}
in.closeEntry();
in.close();
return buffer;
}
getSize() が -1 となった。圧縮時に setSize() を実行してもうまくいかなかった。 byte[] ba を十分大きくしておき、一旦ファイル全体を読み込んでから int[] buffer に移す方法に変える。
static int[] readZipAllInts(String file) throws IOException {
ZipInputStream in = new ZipInputStream(new FileInputStream(file));
ZipEntry zipEntry = in.getNextEntry();
for (int offset = 0; offset < file_length; ) {
int nbytes = in.read(ba, offset, ba.length - offset);
if (nbytes < 0) break;
offset += nbytes;
}
in.closeEntry();
in.close();
int[] buffer = new int[offset / 4];
IntBuffer intBuf = ByteBuffer.wrap(ba).order(ByteOrder.BIG_ENDIAN).asIntBuffer();
intBuf.get(buffer);
return buffer;
}
プログラムは簡単であるが、ファイル読み込み時間の短縮は期待できないようである。
#0 /storage/C060-A4CD/GIS/landsL2/3/1.dat 13ms lands 8/226/98 5recs 24ms #1 /storage/C060-A4CD/GIS/japanMz6/56/24.zip 554ms getOSMs japanMz/8/226/98 out=3086/3086 skip=1258 558ms lands 8/226/99 4recs 575ms lands 8/226/101 9recs 573ms Background concurrent copying GC freed 806298(51MB) AllocSpace objects, 127(107MB) LOS objects, 35% free, 173MB/269MB, paused 74us,18us total 117.021ms JNI critical lock held for 23.100ms on Thread[20,tid=5572,Runnable,Thread*=0xb400006f641770f0,peer=0x17800308,"Thread-14"] #2 /storage/C060-A4CD/GIS/japanMz6/56/25.zip 1266ms getOSMs japanMz/8/226/101 out=14939/14939 skip=12852 1278ms getOSMs japanMz/8/226/99 out=13443/13443 skip=6472 6ms lands 8/226/100 4recs 1866ms getOSMs japanMz/8/226/100 out=19872/19872 skip=7336 29ms
#0 /storage/C060-A4CD/GIS/landsL2/3/1.dat 17ms lands 8/226/98 5recs 180ms lands 8/226/100 4recs 160ms lands 8/226/101 9recs 157ms #1 /storage/C060-A4CD/GIS/japanM6/56/24.dat 704ms getOSMs japanM/8/226/98 out=3086/3086 skip=1258 728ms lands 8/227/98 5recs 862ms #2 /storage/C060-A4CD/GIS/japanM6/56/25.dat 621ms getOSMs japanM/8/226/100 out=19872/19872 skip=7336 662ms