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.map;

import uk.me.parabola.imgfmt.FileExistsException;
import uk.me.parabola.imgfmt.FileNotWritableException;
import uk.me.parabola.imgfmt.FileSystemParam;
import uk.me.parabola.imgfmt.Utils;
import uk.me.parabola.imgfmt.app.Area;
import uk.me.parabola.imgfmt.app.ImgFile;
import uk.me.parabola.imgfmt.app.Label;
import uk.me.parabola.imgfmt.app.lbl.LBLFile;
import uk.me.parabola.imgfmt.app.net.NETFile;
import uk.me.parabola.imgfmt.app.net.NODFile;
import uk.me.parabola.imgfmt.app.srt.Sort;
import uk.me.parabola.imgfmt.app.trergn.InternalFiles;
import uk.me.parabola.imgfmt.app.trergn.MapObject;
import uk.me.parabola.imgfmt.app.trergn.PointOverview;
import uk.me.parabola.imgfmt.app.trergn.PolygonOverview;
import uk.me.parabola.imgfmt.app.trergn.PolylineOverview;
import uk.me.parabola.imgfmt.app.trergn.RGNFile;
import uk.me.parabola.imgfmt.app.trergn.Subdivision;
import uk.me.parabola.imgfmt.app.trergn.TREFile;
import uk.me.parabola.imgfmt.app.trergn.Zoom;
import uk.me.parabola.imgfmt.fs.DirectoryEntry;
import uk.me.parabola.imgfmt.fs.FileSystem;
import uk.me.parabola.imgfmt.sys.ImgFS;
import uk.me.parabola.log.Logger;
import uk.me.parabola.mkgmap.combiners.OverviewBuilder;
import uk.me.parabola.util.Configurable;
import uk.me.parabola.util.EnhancedProperties;

/**
 * Holder for a complete map.  A map is made up of several files which
 * include at least the TRE, LBL and RGN files.
 *
 * It is the interface for all information about the whole map, such as the
 * point overviews etc.  Subdivision will hold the map elements.
 *
 * <p>Needless to say, it has nothing to do with java.util.Map and given
 * how it has turned out, with all reading functionality in MapReader
 * it would have been better named MapWriter.
 *
 * @author Steve Ratcliffe
 */

public class Map implements InternalFiles, Configurable {
        private static final Logger log = Logger.getLogger(Map.class);
        private String filename;
        private String mapName;
        private FileSystem fileSystem;

        private TREFile treFile;
        private RGNFile rgnFile;
        private LBLFile lblFile;
        private NETFile netFile;
        private NODFile nodFile;

        // Use createMap() or loadMap() instead of creating a map directly.
        private Map() {
        }

        /**
         * Create a complete map.  This consists of (at least) three
         * files that all have the same basename and different extensions.
         *
         * @param mapname The name of the map.  This is an 8 digit number as a
         * string.
         * @param params Parameters that describe the file system that the map
         * will be created in.
         * @return A map object that holds together all the files that make it up.
         * @throws FileExistsException If the file already exists and we do not
         * want to overwrite it.
         * @throws FileNotWritableException If the file cannot
         * be opened for write.
         */

        public static Map createMap(String mapname, String outputdir, FileSystemParam params, String mapnumber, Sort sort)
                        throws FileExistsException, FileNotWritableException
        {
                Map m = new Map();
                m.mapName = mapname;
                String outFilename = Utils.joinPath(outputdir, mapname, "img");

                FileSystem fs = ImgFS.createFs(outFilename, params);
                m.filename = outFilename;
                m.fileSystem = fs;

                m.rgnFile = new RGNFile(m.fileSystem.create(mapnumber + ".RGN"));
                m.treFile = new TREFile(m.fileSystem.create(mapnumber + ".TRE"));
                m.lblFile = new LBLFile(m.fileSystem.create(mapnumber + ".LBL"), sort);

                int mapid;
                try {
                        mapid = Integer.parseInt(mapnumber);
                } catch (NumberFormatException e) {
                        mapid = 0;
                }
                m.treFile.setMapId(mapid);
                m.fileSystem = fs;

                return m;
        }

        public void config(EnhancedProperties props) {
                // we don't want routing infos in the overview map (for now)
                if (OverviewBuilder.isOverviewImg(mapName) == false){
                        try {
                                if (props.containsKey("route")) {
                                        addNet();
                                        addNod();
                                } else if (props.containsKey("net")) {
                                        addNet();
                                }
                        } catch (FileExistsException e) {
                                log.warn("Could not add NET and/or NOD sections");
                        }
                }
                treFile.config(props);
        }

        protected void addNet() throws FileExistsException {
                netFile = new NETFile(fileSystem.create(mapName + ".NET"));
        }

        protected void addNod() throws FileExistsException {
                nodFile = new NODFile(fileSystem.create(mapName + ".NOD"), true);
        }

        /**
         * Set the area that the map covers.
         * @param area The outer bounds of the map.
         */

        public void setBounds(Area area) {
                treFile.setBounds(area);
        }

        /**
         * Add a copyright message to the map.
         * @param str the copyright message. The second (last?) one set
         * gets shown when the device starts (sometimes?).
         */

        public void addCopyright(String str) {
                Label cpy = lblFile.newLabel(str);
                treFile.addCopyright(cpy);
        }

        /**
         * There is an area after the TRE header and before its data
         * starts that can be used to save any old junk it seems.
         *
         * @param info Any string.
         */

        public void addInfo(String info) {
                treFile.addInfo(info);
        }

        /**
         * Create a new zoom level. The level 0 is the most detailed and
         * level 15 is the most general.  Most maps would just have 4
         * different levels or less.  We are just having two to start with
         * but will probably advance to at least 3.
         *
         * @param level The zoom level, and integer between 0 and 15. Its
         * like a logical zoom level.
         * @param bits  The number of bits per coordinate, a measure of
         * the actual amount of detail that will be in the level.  So this
         * is like a physical zoom level.
         * @return The zoom object.
         */

        public Zoom createZoom(int level, int bits) {
                return treFile.createZoom(level, bits);
        }

        /**
         * Create the top level division. It must be empty afaik and cover
         * the whole area of the map.
         *
         * @param area The whole map area.
         * @param zoom The zoom level that you want the top level to be
         * at.  Its going to be at least level 1.
         * @return The top level division.
         */

        public Subdivision topLevelSubdivision(Area area, Zoom zoom) {
                zoom.setInherited(true); // May not always be necessary/desired

                InternalFiles ifiles = this;
                Subdivision sub = Subdivision.topLevelSubdivision(ifiles, area, zoom);
                rgnFile.startDivision(sub);
                return sub;
        }

        /**
         * Create a subdivision that is beneath the top level.  We have to
         * pass the parent division.
         * <p>
         * Note that you cannot create these all up front.  You must
         * create it, fill it will its map elements and then create the
         * next one.  You must also start at the top level and work down.
         *
         * @param parent The parent subdivision.
         * @param area The area of the new child subdiv.
         * @param zoom The zoom level of the child.
         * @return The new division.
         */

        public Subdivision createSubdivision(Subdivision parent, Area area, Zoom zoom)
        {
                log.debug("creating division");
                return parent.createSubdivision(this, area, zoom);
        }

        public void addPointOverview(PointOverview ov) {
                treFile.addPointOverview(ov);
        }

        public void addPolylineOverview(PolylineOverview ov) {
                treFile.addPolylineOverview(ov);
        }

        public void addPolygonOverview(PolygonOverview ov) {
                treFile.addPolygonOverview(ov);
        }

        /**
         * Adds the bits to the point of interest flags.
         * @param flags The POI flags.
         */

        public void addPoiDisplayFlags(int flags) {
                treFile.addPoiDisplayFlags((byte) flags);
        }

        public void addMapObject(MapObject item) {
                rgnFile.addMapObject(item);
        }

        public void setSort(Sort sort) {
                lblFile.setSort(sort);
                if (netFile != null)
                        netFile.setSort(sort);
        }

        public void setLabelCharset(String desc, boolean forceUpper) {
                lblFile.setCharacterType(desc, forceUpper);
        }
       
        /**
         * Close this map by closing all the constituent files.
         *
         * Some history:
         */

        public void close() {
                ImgFile[] files = {
                                rgnFile, treFile, lblFile,
                                netFile, nodFile
                };

                int headerSlotsRequired = 0;

                FileSystemParam param = fileSystem.fsparam();
                int blockSize = param.getBlockSize();

                for (ImgFile f : files) {
                        if (f == null)
                                continue;

                        long len = f.getSize();
                        log.debug("img file len=", len);

                        // Blocks required for this file
                        int nBlocks = (int) ((len + blockSize - 1) / blockSize);

                        // Now we calculate how many directory blocks we need, you have
                        // to round up as files do not share directory blocks.
                        headerSlotsRequired += (nBlocks + DirectoryEntry.SLOTS_PER_ENTRY - 1)/DirectoryEntry.SLOTS_PER_ENTRY;
                }

                log.debug("header slots required", headerSlotsRequired);

                // A header slot is always 512 bytes, so we need to calculate the
                // number of blocks if the block-size is different.
                // There are 2 slots for the header itself.
                int blocksRequired = 2 + headerSlotsRequired * 512 / blockSize;

                param.setReservedDirectoryBlocks(blocksRequired);
                fileSystem.fsparam(param);

                for (ImgFile f : files)
                        Utils.closeFile(f);

                fileSystem.close();
        }

        public String getFilename() {
                return filename;
        }

        public RGNFile getRgnFile() {
                return rgnFile;
        }

        public LBLFile getLblFile() {
                return lblFile;
        }

        public TREFile getTreFile() {
                return treFile;
        }

        public NETFile getNetFile() {
                return netFile;
        }

        public NODFile getNodFile() {
                return nodFile;
        }
}