Subversion Repositories mkgmap

Rev

Rev 3408 | Blame | Compare with Previous | Last modification | View Log | RSS feed

/*
 * 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);
        }
}