自作地図アプリのバイナリレコードファイルはシンプルな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