/* * Copyright (C) 2006 Steve Ratcliffe * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program 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 General Public License for more details. * * * Author: Steve Ratcliffe * Create date: 03-Dec-2006 */ package uk.me.parabola.imgfmt.app.trergn; import java.io.ByteArrayOutputStream; import java.io.IOException; import uk.me.parabola.imgfmt.app.BufferedImgFileWriter; import uk.me.parabola.imgfmt.app.ImgFile; import uk.me.parabola.imgfmt.app.ImgFileWriter; import uk.me.parabola.imgfmt.fs.ImgChannel; import uk.me.parabola.log.Logger; /** * The region file. Holds actual details of points and lines etc. * * * * The data is rather complicated and is packed to save space. This class does * not really handle that format however as it is written by the * {@link MapObject}s themselves. * * Each subdivision takes space in this file. The I am expecting this to be the * biggest file, although it seems that TRE may be in some circumstances. * * @author Steve Ratcliffe */ public class RGNFile extends ImgFile { private static final Logger log = Logger.getLogger(RGNFile.class); private static final int HEADER_LEN = RGNHeader.HEADER_LEN; private final RGNHeader header = new RGNHeader(); private Subdivision currentDivision; private int indPointPtrOff; private int polylinePtrOff; private int polygonPtrOff; private ByteArrayOutputStream extTypePointsData; private ByteArrayOutputStream extTypeLinesData; private ByteArrayOutputStream extTypeAreasData; public RGNFile(ImgChannel chan) { setHeader(header); setWriter(new BufferedImgFileWriter(chan)); // Position at the start of the writable area. position(HEADER_LEN); } public void write() { if (!isWritable()) throw new IllegalStateException("File not writable"); header.setDataSize(position() - HEADER_LEN); if(extTypeAreasData != null) { header.setExtTypeAreasInfo(position(), extTypeAreasData.size()); getWriter().put(extTypeAreasData.toByteArray()); } if(extTypeLinesData != null) { header.setExtTypeLinesInfo(position(), extTypeLinesData.size()); getWriter().put(extTypeLinesData.toByteArray()); } if(extTypePointsData != null) { header.setExtTypePointsInfo(position(), extTypePointsData.size()); getWriter().put(extTypePointsData.toByteArray()); } getHeader().writeHeader(getWriter()); } public void startDivision(Subdivision sd) { sd.setStartRgnPointer(position() - HEADER_LEN); // We need to reserve space for a pointer for each type of map // element that is supported by this division. Note that these // pointers are only 2bytes long. A pointer to the points is never // needed as it will always be first if present. if (sd.needsIndPointPtr()) { indPointPtrOff = position(); position(position() + 2); } if (sd.needsPolylinePtr()) { polylinePtrOff = position(); position(position() + 2); } if (sd.needsPolygonPtr()) { polygonPtrOff = position(); position(position() + 2); } currentDivision = sd; } public void addMapObject(MapObject item) { if(item.hasExtendedType()) { try { if(item instanceof Point) { if(extTypePointsData == null) extTypePointsData = new ByteArrayOutputStream(); item.write(extTypePointsData); } else if(item instanceof Polygon) { if(extTypeAreasData == null) extTypeAreasData = new ByteArrayOutputStream(); item.write(extTypeAreasData); } else if(item instanceof Polyline) { if(extTypeLinesData == null) extTypeLinesData = new ByteArrayOutputStream(); item.write(extTypeLinesData); } else log.error("Can't add object of type " + item.getClass()); } catch (IOException ioe) { log.error("Error writing extended type object: " + ioe.getMessage()); } } else { item.write(getWriter()); } } public void setIndPointPtr() { if (currentDivision.needsIndPointPtr()) { long currPos = position(); position(indPointPtrOff); long off = currPos - currentDivision.getStartRgnPointer() - HEADER_LEN; if (off > 0xffff) throw new IllegalStateException("IndPoint offset too large: " + off); getWriter().putChar((char) off); position(currPos); } } public void setPolylinePtr() { if (currentDivision.needsPolylinePtr()) { long currPos = position(); position(polylinePtrOff); long off = currPos - currentDivision.getStartRgnPointer() - HEADER_LEN; if (off > 0xffff) throw new IllegalStateException("Polyline offset too large: " + off); if (log.isDebugEnabled()) log.debug("setting polyline offset to", off); getWriter().putChar((char) off); position(currPos); } } public void setPolygonPtr() { if (currentDivision.needsPolygonPtr()) { long currPos = position(); long off = currPos - currentDivision.getStartRgnPointer() - HEADER_LEN; log.debug("currpos=", currPos, ", off=", off); if (off > 0xffff) throw new IllegalStateException("Polygon offset too large: " + off); if (log.isDebugEnabled()) log.debug("setting polygon offset to ", off, " @", polygonPtrOff); position(polygonPtrOff); getWriter().putChar((char) off); position(currPos); } } public ImgFileWriter getWriter() { return super.getWriter(); } public int getExtTypePointsSize() { return (extTypePointsData == null)? 0 : extTypePointsData.size(); } public int getExtTypeLinesSize() { return (extTypeLinesData == null)? 0 : extTypeLinesData.size(); } public int getExtTypeAreasSize() { return (extTypeAreasData == null)? 0 : extTypeAreasData.size(); } public boolean haveExtendedTypes() { return (extTypePointsData != null || extTypeLinesData != null || extTypeAreasData != null); } }