/* * Geotoolkit - An Open Source Java GIS Toolkit * http://www.geotoolkit.org * * (C) 2002-2008, Open Source Geospatial Foundation (OSGeo) * (C) 2009-2010, Geomatys * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. */ package org.geotoolkit.data.shapefile.shp; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.channels.FileChannel; import org.apache.sis.feature.SingleAttributeTypeBuilder; import org.apache.sis.util.Classes; import org.opengis.feature.AttributeType; import org.opengis.referencing.crs.CoordinateReferenceSystem; /** * * @author jamesm * @author Ian Schneider * @author Johann Sorel (Geomatys) * @module */ public class ShapefileHeader { public static final int MAGIC = 9994; public static final int VERSION = 1000; private final int fileLength; private final int version; private final ShapeType shapeType; private final double minX; private final double maxX; private final double minY; private final double maxY; public ShapefileHeader(final int fileLenght, final int version, final ShapeType shapeType, final double minX, final double maxX, final double minY, final double maxY){ this.fileLength = fileLenght; this.version = version; this.shapeType = shapeType; this.minX = minX; this.maxX = maxX; this.minY = minY; this.maxY = maxY; } public ShapeType getShapeType() { return shapeType; } public int getVersion() { return version; } public int getFileLength() { return fileLength; } public double minX() { return minX; } public double minY() { return minY; } public double maxX() { return maxX; } public double maxY() { return maxY; } /** * * @param namespace * @return AttributeDescriptor mapping this header definition */ public AttributeType createDescriptor(final String namespace, final CoordinateReferenceSystem crs){ final SingleAttributeTypeBuilder buildAtt = new SingleAttributeTypeBuilder(); final Class<?> geometryClass = getShapeType().bestJTSClass(); buildAtt.setName(namespace, Classes.getShortName(geometryClass)); buildAtt.setCRS(crs); buildAtt.setValueClass(geometryClass); buildAtt.setName(namespace, "the_geom"); return buildAtt.build(); } @Override public String toString() { final StringBuilder sb = new StringBuilder("ShapeFileHeader["); sb.append(" size ").append(fileLength); sb.append(" version ").append(version); sb.append(" shapeType ").append(shapeType); sb.append(" bounds ").append(minX).append(',').append(minY).append(',').append(maxX).append(',').append(maxY); sb.append(" ]"); return sb.toString(); } private static void checkMagic(final int fileCode, final boolean strict) throws IOException { if (fileCode != MAGIC) { final String message = "Wrong magic number, expected " + MAGIC + ", got " + fileCode; if (!strict) { System.err.println(message); } else { throw new IOException(message); } } } private static void checkVersion(final int version,final boolean strict) throws IOException { if (version != VERSION) { final String message = "Wrong version, expected " + MAGIC + ", got " + version; if (!strict) { System.err.println(message); } else { throw new IOException(message); } } } /** * Read the header from the given ByteBuffer. * SHP and SHX share the same header structure. * * @param buffer * @param strict : will check version if true * @return * @throws IOException */ public static ShapefileHeader read(final ByteBuffer buffer, final boolean strict) throws IOException { buffer.order(ByteOrder.BIG_ENDIAN); final int fileCode = buffer.getInt(); checkMagic(fileCode,strict); // skip 5 ints... buffer.position(buffer.position() + 20); final int fileLength = buffer.getInt(); buffer.order(ByteOrder.LITTLE_ENDIAN); final int version = buffer.getInt(); checkVersion(version,strict); final ShapeType shapeType = ShapeType.forID(buffer.getInt()); final double minX = buffer.getDouble(); final double minY = buffer.getDouble(); final double maxX = buffer.getDouble(); final double maxY = buffer.getDouble(); // skip remaining unused bytes buffer.order(ByteOrder.BIG_ENDIAN); // well they may not be unused forever... buffer.position(buffer.position() + 32); return new ShapefileHeader(fileLength, version, shapeType, minX, maxX, minY, maxY); } /** * Write header in the given ByteBuffer. */ public static void write(final ByteBuffer buffer, final ShapeType type, final int length, final double minX, final double minY, final double maxX, final double maxY) throws IOException { buffer.order(ByteOrder.BIG_ENDIAN); buffer.putInt(MAGIC); // Skip unused part of header for (int i=0; i<5; i++) { buffer.putInt(0); } buffer.putInt(length); buffer.order(ByteOrder.LITTLE_ENDIAN); buffer.putInt(VERSION); buffer.putInt(type.id); // write the bounding box buffer.putDouble(minX); buffer.putDouble(minY); buffer.putDouble(maxX); buffer.putDouble(maxY); // skip remaining unused bytes buffer.order(ByteOrder.BIG_ENDIAN); for (int i=0; i<8; i++) { buffer.putInt(0); // Skip unused part of header } } public static void main(final String[] args) throws Exception { final FileChannel channel = new FileInputStream(new File(args[0])).getChannel(); System.out.println(ShapefileReader.readHeader(channel, true)); channel.close(); } }