/* * Copyright (c) 2016, Metron, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of Metron, Inc. nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL METRON, INC. BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package com.metsci.glimpse.dnc.convert; import static com.jogamp.common.nio.Buffers.SIZEOF_DOUBLE; import static com.jogamp.common.nio.Buffers.SIZEOF_INT; import static com.jogamp.common.nio.Buffers.SIZEOF_LONG; import static com.metsci.glimpse.dnc.convert.Flat.doublesPerFlatLibrary; import static com.metsci.glimpse.dnc.convert.Flat.doublesPerFlatVertex; import static com.metsci.glimpse.dnc.convert.Flat.flatAttrNamesFilename; import static com.metsci.glimpse.dnc.convert.Flat.flatAttrsFilename; import static com.metsci.glimpse.dnc.convert.Flat.flatCharsetFilename; import static com.metsci.glimpse.dnc.convert.Flat.flatChunksFilename; import static com.metsci.glimpse.dnc.convert.Flat.flatCoverageNamesFilename; import static com.metsci.glimpse.dnc.convert.Flat.flatFcodeNamesFilename; import static com.metsci.glimpse.dnc.convert.Flat.flatFeaturesFilename; import static com.metsci.glimpse.dnc.convert.Flat.flatLibrariesFilename; import static com.metsci.glimpse.dnc.convert.Flat.flatLibraryNamesFilename; import static com.metsci.glimpse.dnc.convert.Flat.flatRingsFilename; import static com.metsci.glimpse.dnc.convert.Flat.flatStringsFilename; import static com.metsci.glimpse.dnc.convert.Flat.flatVerticesFilename; import static com.metsci.glimpse.dnc.convert.Flat.intsPerFlatChunk; import static com.metsci.glimpse.dnc.convert.Flat.intsPerFlatFeature; import static com.metsci.glimpse.dnc.convert.Flat.intsPerFlatRing; import static com.metsci.glimpse.dnc.convert.Flat.longsPerFlatAttr; import static com.metsci.glimpse.dnc.convert.Flat.writeFlatCharset; import static com.metsci.glimpse.dnc.convert.Flat.writeFlatChecksum; import static com.metsci.glimpse.dnc.convert.Flat.FlatAttrType.FLAT_DOUBLE_ATTR; import static com.metsci.glimpse.dnc.convert.Flat.FlatAttrType.FLAT_INT_ATTR; import static com.metsci.glimpse.dnc.convert.Flat.FlatAttrType.FLAT_PACKED_STRING_ATTR; import static com.metsci.glimpse.dnc.convert.Flat.FlatAttrType.FLAT_STRING_ATTR; import static com.metsci.glimpse.dnc.convert.Flat.FlatFeatureType.FLAT_AREA_FEATURE; import static com.metsci.glimpse.dnc.convert.Flat.FlatFeatureType.FLAT_LINE_FEATURE; import static com.metsci.glimpse.dnc.convert.Flat.FlatFeatureType.FLAT_POINT_FEATURE; import static com.metsci.glimpse.dnc.convert.Vpf.createPrimitiveDatas; import static com.metsci.glimpse.dnc.convert.Vpf.findDhtFile; import static com.metsci.glimpse.dnc.convert.Vpf.readAllFeatureClasses; import static com.metsci.glimpse.dnc.convert.Vpf.vpfAreaRings; import static com.metsci.glimpse.dnc.convert.Vpf.vpfDatabaseDirsByName; import static com.metsci.glimpse.dnc.convert.Vpf.vpfLibraryNameComparator; import static com.metsci.glimpse.dnc.convert.Vpf.vpfLineVertices; import static com.metsci.glimpse.dnc.convert.Vpf.vpfPointVertex; import static com.metsci.glimpse.dnc.util.DncMiscUtils.createAndMemmapReadWrite; import static com.metsci.glimpse.dnc.util.DncMiscUtils.createNewDir; import static com.metsci.glimpse.dnc.util.DncMiscUtils.isFilenameCaseSensitive; import static com.metsci.glimpse.dnc.util.DncMiscUtils.packBytesIntoLong; import static com.metsci.glimpse.dnc.util.DncMiscUtils.sorted; import static com.metsci.glimpse.dnc.util.DncMiscUtils.writeIdsMapFile; import static com.metsci.glimpse.util.logging.LoggerUtils.getLogger; import static java.lang.Double.doubleToLongBits; import static java.nio.file.FileVisitOption.FOLLOW_LINKS; import static java.nio.file.FileVisitResult.CONTINUE; import static java.nio.file.Files.createSymbolicLink; import static java.nio.file.Files.delete; import static java.nio.file.Files.exists; import static java.nio.file.Files.walkFileTree; import java.io.File; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.DoubleBuffer; import java.nio.IntBuffer; import java.nio.LongBuffer; import java.nio.MappedByteBuffer; import java.nio.charset.Charset; import java.nio.file.FileVisitResult; import java.nio.file.Path; import java.nio.file.SimpleFileVisitor; import java.nio.file.attribute.BasicFileAttributes; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.Collection; import java.util.EnumSet; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.logging.Logger; import com.google.common.io.Files; import gov.nasa.worldwind.formats.vpf.VPFBasicFeatureFactory; import gov.nasa.worldwind.formats.vpf.VPFCoverage; import gov.nasa.worldwind.formats.vpf.VPFDatabase; import gov.nasa.worldwind.formats.vpf.VPFFeature; import gov.nasa.worldwind.formats.vpf.VPFFeatureClass; import gov.nasa.worldwind.formats.vpf.VPFFeatureFactory; import gov.nasa.worldwind.formats.vpf.VPFLibrary; import gov.nasa.worldwind.formats.vpf.VPFPrimitiveData; import gov.nasa.worldwind.formats.vpf.VPFTile; import gov.nasa.worldwind.geom.LatLon; import it.unimi.dsi.fastutil.objects.Object2IntLinkedOpenHashMap; import it.unimi.dsi.fastutil.objects.Object2IntMap; public class Vpf2Flat { protected static final Logger logger = getLogger( Vpf2Flat.class ); public static void convertVpfToFlat( File vpfParentDir, File flatParentDir, Charset charset ) throws IOException { flatParentDir.mkdirs( ); for ( File vpfDir : vpfDatabaseDirsByName( vpfParentDir ).values( ) ) { Database database = readVpfDatabase( vpfDir ); String dirname = database.name.toLowerCase( ).replace( "dnc", "dncflat" ); File flatDir = createNewDir( flatParentDir, dirname ); writeFlatDatabase( database, flatDir, charset ); } } // Data classes // public static class Database { public String name; public List<Library> libraries = new ArrayList<>( ); } public static class Library { public String name; public double minLat_DEG; public double maxLat_DEG; public double minLon_DEG; public double maxLon_DEG; public Map<String,List<Feature>> featuresByCoverage = new LinkedHashMap<>( ); } public static abstract class Feature { public String fcode; public List<Attribute> attrs = new ArrayList<>( ); } public static class AreaFeature extends Feature { public List<List<Vertex>> rings = new ArrayList<>( ); } public static class LineFeature extends Feature { public List<Vertex> vertices = new ArrayList<>( ); } public static class PointFeature extends Feature { public Vertex vertex = new Vertex( ); } public static abstract class Attribute { public String name; } public static class StringAttribute extends Attribute { public String value; } public static class DoubleAttribute extends Attribute { public double value; } public static class IntAttribute extends Attribute { public int value; } public static class Vertex { public double lat_DEG = Double.NaN; public double lon_DEG = Double.NaN; } // Read VPF // public static Database readVpfDatabase( File databaseDir ) throws IOException { Set<Path> symlinks = new LinkedHashSet<>( ); try { // Create lowercase symlinks, so Worldwind's VPF reader can find them if ( isFilenameCaseSensitive( new File( databaseDir, "test" ) ) ) { walkFileTree( databaseDir.toPath( ), EnumSet.of( FOLLOW_LINKS ), Integer.MAX_VALUE, new SimpleFileVisitor<Path>( ) { public FileVisitResult preVisitDirectory( Path dir, BasicFileAttributes attrs ) throws IOException { createLowercaseSymlink( dir ); return CONTINUE; } public FileVisitResult visitFile( Path file, BasicFileAttributes attrs ) throws IOException { createLowercaseSymlink( file ); return CONTINUE; } private void createLowercaseSymlink( Path path ) throws IOException { Path filename = path.getFileName( ); Path lowercase = path.resolveSibling( filename.toString( ).toLowerCase( ) ); if ( !exists( lowercase ) ) { Path symlink = createSymbolicLink( lowercase, filename ); symlinks.add( symlink ); } } } ); } // Read the VPF files File dhtFile = findDhtFile( databaseDir ); VPFDatabase vpfDatabase = VPFDatabase.fromFile( dhtFile.getPath( ) ); return readVpfDatabase( vpfDatabase ); } finally { // Delete the symlinks we created IOException firstException = null; for ( Path symlink : symlinks ) { try { delete( symlink ); } catch ( IOException e ) { if ( firstException == null ) { firstException = e; } else { firstException.addSuppressed( e ); } } } if ( firstException != null ) { throw firstException; } } } public static Database readVpfDatabase( VPFDatabase database ) { Database result = new Database( ); result.name = database.getName( ); readVpfLibraries( database.getLibraries( ), result.libraries ); return result; } public static void readVpfLibraries( Collection<VPFLibrary> libraries, Collection<Library> results ) { for ( VPFLibrary library : sorted( libraries, vpfLibraryNameComparator ) ) { VPFFeatureClass[] featureClasses = readAllFeatureClasses( library ); if ( featureClasses == null || featureClasses.length == 0 ) continue; Library result = new Library( ); result.name = library.getName( ); result.minLat_DEG = library.getBounds( ).getYmin( ); result.maxLat_DEG = library.getBounds( ).getYmax( ); result.minLon_DEG = library.getBounds( ).getXmin( ); result.maxLon_DEG = library.getBounds( ).getXmax( ); // Null is the pseudo-tile for untiled libraries VPFTile[] tiles = ( library.hasTiledCoverages( ) ? library.getTiles( ) : new VPFTile[] { null } ); for ( VPFTile tile : tiles ) { Map<VPFCoverage,VPFPrimitiveData> primitiveDatas = createPrimitiveDatas( library, tile ); for ( VPFFeatureClass featureClass : featureClasses ) { if ( featureClass == null ) continue; VPFPrimitiveData primitiveData = primitiveDatas.get( featureClass.getCoverage( ) ); if ( primitiveData == null ) continue; VPFFeatureFactory featureFactory = new VPFBasicFeatureFactory( tile, primitiveData ); Collection<? extends VPFFeature> features = featureClass.createFeatures( featureFactory ); if ( features == null ) continue; String coverage = featureClass.getCoverage( ).getName( ); if ( !result.featuresByCoverage.containsKey( coverage ) ) { result.featuresByCoverage.put( coverage, new ArrayList<Feature>( ) ); } readVpfFeatures( features, primitiveData, result.featuresByCoverage.get( coverage ) ); } } results.add( result ); } } public static void readVpfFeatures( Iterable<? extends VPFFeature> features, VPFPrimitiveData primitiveData, Collection<Feature> results ) { for ( VPFFeature feature : features ) { switch ( feature.getType( ) ) { case AREA: { AreaFeature result = new AreaFeature( ); result.fcode = fcode( feature ); readVpfAttrs( feature.getEntries( ), result.attrs ); for ( List<LatLon> ring : vpfAreaRings( feature, primitiveData ) ) { List<Vertex> resultRing = new ArrayList<>( ); for ( LatLon vertex : ring ) { Vertex resultVertex = new Vertex( ); resultVertex.lat_DEG = vertex.latitude.degrees; resultVertex.lon_DEG = vertex.longitude.degrees; resultRing.add( resultVertex ); } result.rings.add( resultRing ); } results.add( result ); } break; case LINE: { LineFeature result = new LineFeature( ); result.fcode = fcode( feature ); readVpfAttrs( feature.getEntries( ), result.attrs ); for ( LatLon vertex : vpfLineVertices( feature, primitiveData ) ) { Vertex resultVertex = new Vertex( ); resultVertex.lat_DEG = vertex.latitude.degrees; resultVertex.lon_DEG = vertex.longitude.degrees; result.vertices.add( resultVertex ); } results.add( result ); } break; case POINT: { PointFeature result = new PointFeature( ); result.fcode = fcode( feature ); readVpfAttrs( feature.getEntries( ), result.attrs ); LatLon vertex = vpfPointVertex( feature, primitiveData ); result.vertex.lat_DEG = vertex.latitude.degrees; result.vertex.lon_DEG = vertex.longitude.degrees; results.add( result ); } break; default: { // Skip } break; } } } public static void readVpfAttrs( Iterable<Entry<String,Object>> attrs, Collection<Attribute> results ) { for ( Entry<String,Object> attr : attrs ) { String name = attr.getKey( ); Object value = attr.getValue( ); if ( value instanceof String ) { StringAttribute result = new StringAttribute( ); result.name = name; result.value = ( String ) value; results.add( result ); } else if ( value instanceof Double ) { DoubleAttribute result = new DoubleAttribute( ); result.name = name; result.value = ( ( Double ) value ).doubleValue( ); results.add( result ); } else if ( value instanceof Integer ) { IntAttribute result = new IntAttribute( ); result.name = name; result.value = ( ( Integer ) value ).intValue( ); results.add( result ); } else { throw new RuntimeException( "Can't handle attr-value of this type: name = " + name + ", value-type = " + value.getClass( ).getName( ) ); } } } public static String fcode( VPFFeature feature ) { return feature.getStringValue( "f_code" ); } // Write Flat // public static void writeFlatDatabase( Database database, File flatDir, Charset charset ) throws IOException { // Output Files File chunksFile = new File( flatDir, flatChunksFilename ); File librariesFile = new File( flatDir, flatLibrariesFilename ); File featuresFile = new File( flatDir, flatFeaturesFilename ); File ringsFile = new File( flatDir, flatRingsFilename ); File verticesFile = new File( flatDir, flatVerticesFilename ); File attrsFile = new File( flatDir, flatAttrsFilename ); File stringsFile = new File( flatDir, flatStringsFilename ); File charsetFile = new File( flatDir, flatCharsetFilename ); File libraryNamesFile = new File( flatDir, flatLibraryNamesFilename ); File coverageNamesFile = new File( flatDir, flatCoverageNamesFilename ); File fcodeNamesFile = new File( flatDir, flatFcodeNamesFilename ); File attrNamesFile = new File( flatDir, flatAttrNamesFilename ); // Charset writeFlatCharset( flatDir, charset ); // Chunks int totalChunkCount = 0; for ( Library library : database.libraries ) { totalChunkCount += library.featuresByCoverage.size( ); } int totalChunksByteCount = totalChunkCount * intsPerFlatChunk * SIZEOF_INT; MappedByteBuffer chunksMapped = createAndMemmapReadWrite( chunksFile, totalChunksByteCount ); IntBuffer chunksBuf = chunksMapped.asIntBuffer( ); // Libraries int totalLibraryCount = database.libraries.size( ); int totalLibrariesByteCount = totalLibraryCount * doublesPerFlatLibrary * SIZEOF_DOUBLE; MappedByteBuffer librariesMapped = createAndMemmapReadWrite( librariesFile, totalLibrariesByteCount ); DoubleBuffer librariesBuf = librariesMapped.asDoubleBuffer( ); // Features int totalFeatureCount = 0; for ( Library library : database.libraries ) { for ( List<Feature> features : library.featuresByCoverage.values( ) ) { totalFeatureCount += features.size( ); } } int totalFeaturesByteCount = totalFeatureCount * intsPerFlatFeature * SIZEOF_INT; MappedByteBuffer featuresMapped = createAndMemmapReadWrite( featuresFile, totalFeaturesByteCount ); IntBuffer featuresBuf = featuresMapped.asIntBuffer( ); // Rings int totalRingCount = 0; for ( Library library : database.libraries ) { for ( List<Feature> features : library.featuresByCoverage.values( ) ) { for ( Feature feature : features ) { if ( feature instanceof AreaFeature ) { totalRingCount += ( ( AreaFeature ) feature ).rings.size( ); } } } } int totalRingsByteCount = totalRingCount * intsPerFlatRing * SIZEOF_INT; MappedByteBuffer ringsMapped = createAndMemmapReadWrite( ringsFile, totalRingsByteCount ); IntBuffer ringsBuf = ringsMapped.asIntBuffer( ); // Vertices int totalVertexCount = 0; for ( Library library : database.libraries ) { for ( List<Feature> features : library.featuresByCoverage.values( ) ) { for ( Feature feature : features ) { if ( feature instanceof AreaFeature ) { for ( List<Vertex> ring : ( ( AreaFeature ) feature ).rings ) { totalVertexCount += ring.size( ); } } else if ( feature instanceof LineFeature ) { totalVertexCount += ( ( LineFeature ) feature ).vertices.size( ); } else if ( feature instanceof PointFeature ) { totalVertexCount += 1; } else { throw new RuntimeException( "Can't handle feature of this type: type = " + feature.getClass( ).getName( ) ); } } } } int totalVerticesByteCount = totalVertexCount * doublesPerFlatVertex * SIZEOF_DOUBLE; MappedByteBuffer verticesMapped = createAndMemmapReadWrite( verticesFile, totalVerticesByteCount ); DoubleBuffer verticesBuf = verticesMapped.asDoubleBuffer( ); // Attrs int totalAttrCount = 0; for ( Library library : database.libraries ) { for ( List<Feature> features : library.featuresByCoverage.values( ) ) { for ( Feature feature : features ) { totalAttrCount += feature.attrs.size( ); } } } int totalAttrsByteCount = totalAttrCount * longsPerFlatAttr * SIZEOF_LONG; MappedByteBuffer attrsMapped = createAndMemmapReadWrite( attrsFile, totalAttrsByteCount ); LongBuffer attrsBuf = attrsMapped.asLongBuffer( ); // Strings int totalStringsByteCount = 0; for ( Library library : database.libraries ) { for ( List<Feature> features : library.featuresByCoverage.values( ) ) { for ( Feature feature : features ) { for ( Attribute attr : feature.attrs ) { if ( attr instanceof StringAttribute ) { byte[] bytes = ( ( StringAttribute ) attr ).value.getBytes( charset ); if ( bytes.length > 7 ) { totalStringsByteCount += bytes.length; } } } } } } MappedByteBuffer stringsMapped = createAndMemmapReadWrite( stringsFile, totalStringsByteCount ); ByteBuffer stringsBuf = stringsMapped.duplicate( ); // ID Maps Object2IntMap<String> libraryIds = new Object2IntLinkedOpenHashMap<>( ); Object2IntMap<String> coverageIds = new Object2IntLinkedOpenHashMap<>( ); Object2IntMap<String> fcodeIds = new Object2IntLinkedOpenHashMap<>( ); Object2IntMap<String> attrNameIds = new Object2IntLinkedOpenHashMap<>( ); // Put data into buffers for ( Library library : database.libraries ) { int libraryIndex = librariesBuf.position( ) / doublesPerFlatLibrary; libraryIds.put( library.name, libraryIndex ); librariesBuf.put( library.minLat_DEG ) .put( library.maxLat_DEG ) .put( library.minLon_DEG ) .put( library.maxLon_DEG ); for ( Entry<String,List<Feature>> chunk : library.featuresByCoverage.entrySet( ) ) { String coverage = chunk.getKey( ); List<Feature> features = chunk.getValue( ); int coverageId = getOrCreateId( coverageIds, coverage ); int featureFirst = featuresBuf.position( ) / intsPerFlatFeature; int featureCount = features.size( ); chunksBuf.put( libraryIndex ) .put( coverageId ) .put( featureFirst ) .put( featureCount ); for ( Feature feature : features ) { // Fcode // int fcodeId = getOrCreateId( fcodeIds, feature.fcode ); // Attrs // int attrFirst = attrsBuf.position( ) / longsPerFlatAttr; int attrCount = feature.attrs.size( ); for ( Attribute attr : feature.attrs ) { byte attrType; long attrValue; if ( attr instanceof StringAttribute ) { byte[] bytes = ( ( StringAttribute ) attr ).value.getBytes( charset ); if ( bytes.length > 7 ) { attrType = FLAT_STRING_ATTR; int stringsByteFirst = stringsBuf.position( ); int stringsByteCount = bytes.length; stringsBuf.put( bytes, 0, stringsByteCount ); attrValue = ( ( ( ( long ) stringsByteFirst ) & 0xFFFFFFFF ) << 32 ) | ( ( ( long ) stringsByteCount ) & 0xFFFFFFFF ); } else { attrType = FLAT_PACKED_STRING_ATTR; attrValue = packBytesIntoLong( bytes ); } } else if ( attr instanceof DoubleAttribute ) { attrType = FLAT_DOUBLE_ATTR; attrValue = doubleToLongBits( ( ( DoubleAttribute ) attr ).value ); } else if ( attr instanceof IntAttribute ) { attrType = FLAT_INT_ATTR; attrValue = ( ( IntAttribute ) attr ).value; } else { throw new RuntimeException( "Can't handle attr of this type: name = " + attr.name + ", type = " + attr.getClass( ).getName( ) ); } int attrNameId = getOrCreateId( attrNameIds, attr.name ); long attrNameIdAndType = ( ( ( ( long ) attrNameId ) & 0xFFFFFFFF ) << 32 ) | ( ( ( int ) attrType ) & 0xFF ); attrsBuf.put( attrNameIdAndType ).put( attrValue ); } // Delineation & Vertices // byte featureType; int featureItemFirst; int featureItemCount; if ( feature instanceof AreaFeature ) { featureType = FLAT_AREA_FEATURE; AreaFeature areaFeature = ( AreaFeature ) feature; featureItemFirst = ringsBuf.position( ) / intsPerFlatRing; featureItemCount = areaFeature.rings.size( ); for ( List<Vertex> ring : areaFeature.rings ) { int vertexFirst = verticesBuf.position( ) / doublesPerFlatVertex; int vertexCount = ring.size( ); for ( Vertex vertex : ring ) { verticesBuf.put( vertex.lat_DEG ).put( vertex.lon_DEG ); } ringsBuf.put( vertexFirst ).put( vertexCount ); } } else if ( feature instanceof LineFeature ) { featureType = FLAT_LINE_FEATURE; LineFeature lineFeature = ( LineFeature ) feature; featureItemFirst = verticesBuf.position( ) / doublesPerFlatVertex; featureItemCount = lineFeature.vertices.size( ); for ( Vertex vertex : lineFeature.vertices ) { verticesBuf.put( vertex.lat_DEG ).put( vertex.lon_DEG ); } } else if ( feature instanceof PointFeature ) { featureType = FLAT_POINT_FEATURE; PointFeature pointFeature = ( PointFeature ) feature; featureItemFirst = verticesBuf.position( ) / doublesPerFlatVertex; featureItemCount = 1; verticesBuf.put( pointFeature.vertex.lat_DEG ).put( pointFeature.vertex.lon_DEG ); } else { throw new RuntimeException( "Can't handle feature of this type: type = " + feature.getClass( ).getName( ) ); } featuresBuf.put( fcodeId ) .put( ( int ) featureType ) .put( attrFirst ) .put( attrCount ) .put( featureItemFirst ) .put( featureItemCount ); } } } // Flush buffers to disk chunksMapped.force( ); librariesMapped.force( ); featuresMapped.force( ); ringsMapped.force( ); verticesMapped.force( ); attrsMapped.force( ); stringsMapped.force( ); // Make sure we wrote the expected number of bytes to each buffer if ( SIZEOF_INT * chunksBuf.position( ) != totalChunksByteCount ) logger.severe( "Wrong number of bytes written to chunks file: expected = " + totalChunksByteCount + ", found = " + ( SIZEOF_INT * chunksBuf.position( ) ) ); if ( SIZEOF_LONG * librariesBuf.position( ) != totalLibrariesByteCount ) logger.severe( "Wrong number of bytes written to libraries file: expected = " + totalLibrariesByteCount + ", found = " + ( SIZEOF_LONG * librariesBuf.position( ) ) ); if ( SIZEOF_INT * featuresBuf.position( ) != totalFeaturesByteCount ) logger.severe( "Wrong number of bytes written to features file: expected = " + totalFeaturesByteCount + ", found = " + ( SIZEOF_INT * featuresBuf.position( ) ) ); if ( SIZEOF_INT * ringsBuf.position( ) != totalRingsByteCount ) logger.severe( "Wrong number of bytes written to rings file: expected = " + totalRingsByteCount + ", found = " + ( SIZEOF_INT * ringsBuf.position( ) ) ); if ( SIZEOF_DOUBLE * verticesBuf.position( ) != totalVerticesByteCount ) logger.severe( "Wrong number of bytes written to vertices file: expected = " + totalVerticesByteCount + ", found = " + ( SIZEOF_DOUBLE * verticesBuf.position( ) ) ); if ( SIZEOF_LONG * attrsBuf.position( ) != totalAttrsByteCount ) logger.severe( "Wrong number of bytes written to attrs file: expected = " + totalAttrsByteCount + ", found = " + ( SIZEOF_LONG * attrsBuf.position( ) ) ); if ( 1 * stringsBuf.position( ) != totalStringsByteCount ) logger.severe( "Wrong number of bytes written to strings file: expected = " + totalStringsByteCount + ", found = " + ( 1 * stringsBuf.position( ) ) ); // Write ID maps writeIdsMapFile( libraryIds, libraryNamesFile, charset ); writeIdsMapFile( coverageIds, coverageNamesFile, charset ); writeIdsMapFile( fcodeIds, fcodeNamesFile, charset ); writeIdsMapFile( attrNameIds, attrNamesFile, charset ); // Write checksum try { MessageDigest digest = MessageDigest.getInstance( "MD5" ); digest.update( Files.toByteArray( charsetFile ) ); digest.update( Files.toByteArray( libraryNamesFile ) ); digest.update( Files.toByteArray( coverageNamesFile ) ); digest.update( Files.toByteArray( fcodeNamesFile ) ); digest.update( Files.toByteArray( attrNamesFile ) ); digest.update( chunksMapped ); digest.update( librariesMapped ); digest.update( featuresMapped ); digest.update( ringsMapped ); digest.update( verticesMapped ); digest.update( attrsMapped ); digest.update( stringsMapped ); writeFlatChecksum( flatDir, digest.digest( ) ); } catch ( NoSuchAlgorithmException e ) { throw new RuntimeException( e ); } } public static <K> int getOrCreateId( Object2IntMap<K> idsMap, K key ) { if ( !idsMap.containsKey( key ) ) { idsMap.put( key, idsMap.size( ) ); } return idsMap.getInt( key ); } }