Subversion Repositories mkgmap

Rev

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

/*
 * Copyright (C) 2009.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 3 or
 * 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.
 */

package uk.me.parabola.imgfmt.app.mdr;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import uk.me.parabola.imgfmt.app.ImgFileWriter;
import uk.me.parabola.imgfmt.app.Label;
import uk.me.parabola.imgfmt.app.srt.SortKey;

/**
 * The MDR 7 section is a list of all streets.  Only street names are saved
 * and so I believe that the NET section is required to make this work.
 *
 * @author Steve Ratcliffe
 */

public class Mdr7 extends MdrMapSection {
        private List<Mdr7Record> allStreets = new ArrayList<>();
        private List<Mdr7Record> streets = new ArrayList<>();

        public Mdr7(MdrConfig config) {
                setConfig(config);
        }

        public void addStreet(int mapId, String name, int lblOffset, int strOff, Mdr5Record mdrCity) {
                Mdr7Record st = new Mdr7Record();
                st.setMapIndex(mapId);
                st.setLabelOffset(lblOffset);
                st.setStringOffset(strOff);
                st.setName(name);
                st.setCity(mdrCity);
                allStreets.add(st);
        }

        /**
         * Since we change the number of records by removing some after sorting,
         * we sort and de-duplicate here.
         */

        protected void preWriteImpl() {
                List<SortKey<Mdr7Record>> sortedStreets = MdrUtils.sortList(getConfig().getSort(), allStreets);

                // De-duplicate the street names so that there is only one entry
                // per map for the same name.
                int recordNumber = 0;
                Mdr7Record last = new Mdr7Record();
                for (SortKey<Mdr7Record> sk : sortedStreets) {
                        Mdr7Record r = sk.getObject();
                        if (r.getMapIndex() != last.getMapIndex() || !r.getName().equals(last.getName())) {
                                recordNumber++;
                                last = r;
                                r.setIndex(recordNumber);
                                streets.add(r);
                        } else {
                                // This has the same name (and map number) as the previous one. Save the pointer to that one
                                // which is going into the file.
                                r.setIndex(recordNumber);
                        }
                }
        }

        public void writeSectData(ImgFileWriter writer) {
                String lastName = null;
                boolean hasStrings = hasFlag(0x1);
                for (Mdr7Record s : streets) {
                        addIndexPointer(s.getMapIndex(), s.getIndex());

                        putMapIndex(writer, s.getMapIndex());
                        int lab = s.getLabelOffset();
                        String name = Label.stripGarminCodes(s.getName());
                        int trailingFlags = 0;
                        if (!name.equals(lastName)) {
                                lab |= 0x800000;
                                lastName = name;
                                trailingFlags = 1;
                        }
                        writer.put3(lab);
                        if (hasStrings)
                                putStringOffset(writer, s.getStringOffset());
                       
                        writer.put((byte) trailingFlags);
                }
        }

        /**
         * For the map number, label, string (opt), and trailing flags (opt).
         * The trailing flags are variable size. We are just using 1 now.
         */

        public int getItemSize() {
                PointerSizes sizes = getSizes();
                int size = sizes.getMapSize() + 3 + 1;
                if (!isForDevice())
                        size += sizes.getStrOffSize();
                return size;
        }

        protected int numberOfItems() {
                return streets.size();
        }

        /**
         * Value of 3 possibly the existence of the lbl field.
         */

        public int getExtraValue() {
                int magic = 0x42;
                if (isForDevice()) {
                        magic |= 0x4;
                } else {
                        magic |= 0x1; //strings
                }

                return magic;
        }

        protected void releaseMemory() {
                allStreets = null;
                streets = null;
        }

        /**
         * Must be called after the section data is written so that the streets
         * array is already sorted.
         * @return List of index records.
         */

        public List<Mdr8Record> getIndex() {
                List<Mdr8Record> list = new ArrayList<>();
                for (int number = 1; number <= streets.size(); number += 10240) {
                        String prefix = getPrefixForRecord(number);

                        // need to step back to find the first...
                        int rec = number;
                        while (rec > 1) {
                                String p = getPrefixForRecord(rec);
                                if (!p.equals(prefix)) {
                                        rec++;
                                        break;
                                }
                                rec--;
                        }

                        Mdr8Record indexRecord = new Mdr8Record();
                        indexRecord.setPrefix(prefix);
                        indexRecord.setRecordNumber(rec);
                        list.add(indexRecord);
                }
                return list;
        }

        /**
         * Get the prefix of the name at the given record.
         * @param number The record number.
         * @return The first 4 (or whatever value is set) characters of the street
         * name.
         */

        private String getPrefixForRecord(int number) {
                Mdr7Record record = streets.get(number-1);
                int endIndex = MdrUtils.STREET_INDEX_PREFIX_LEN;
                String name = record.getName();
                if (endIndex > name.length()) {
                        StringBuilder sb = new StringBuilder(name);
                        while (sb.length() < endIndex)
                                sb.append('\0');
                        name = sb.toString();
                }
                return name.substring(0, endIndex);
        }

        public List<Mdr7Record> getStreets() {
                return Collections.unmodifiableList(allStreets);
        }
       
        public List<Mdr7Record> getSortedStreets() {
                return Collections.unmodifiableList(streets);
        }

        public void relabelMaps(Mdr1 maps) {
                relabel(maps, allStreets);
        }
}