Subversion Repositories mkgmap

Rev

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

/*
 * Copyright (C) 2010.
 *
 * 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.mkgmap.combiners;

import java.io.File;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import uk.me.parabola.imgfmt.ExitException;
import uk.me.parabola.imgfmt.FileExistsException;
import uk.me.parabola.imgfmt.FileNotWritableException;
import uk.me.parabola.imgfmt.FileSystemParam;
import uk.me.parabola.imgfmt.MapFailedException;
import uk.me.parabola.imgfmt.Utils;
import uk.me.parabola.imgfmt.app.Area;
import uk.me.parabola.imgfmt.app.Coord;
import uk.me.parabola.imgfmt.app.map.Map;
import uk.me.parabola.imgfmt.app.map.MapReader;
import uk.me.parabola.imgfmt.app.srt.Sort;
import uk.me.parabola.imgfmt.app.trergn.Point;
import uk.me.parabola.imgfmt.app.trergn.Polygon;
import uk.me.parabola.imgfmt.app.trergn.Polyline;
import uk.me.parabola.imgfmt.app.trergn.Zoom;
import uk.me.parabola.log.Logger;
import uk.me.parabola.mkgmap.CommandArgs;
import uk.me.parabola.mkgmap.build.MapBuilder;
import uk.me.parabola.mkgmap.general.LevelInfo;
import uk.me.parabola.mkgmap.general.MapLine;
import uk.me.parabola.mkgmap.general.MapPoint;
import uk.me.parabola.mkgmap.general.MapShape;
import uk.me.parabola.mkgmap.srt.SrtTextReader;

/**
 * Build the overview map.  This is a low resolution map that covers the whole
 * of a map set.  It also contains polygons that correspond to the areas
 * covered by the individual map tiles.
 *
 * @author Steve Ratcliffe
 */

public class OverviewBuilder implements Combiner {
        Logger log = Logger.getLogger(OverviewBuilder.class);
        public static final String OVERVIEW_PREFIX = "ovm_";
        private final OverviewMap overviewSource;
        private String areaName;
        private String overviewMapname;
        private String overviewMapnumber;
        private Zoom[] levels;
        private String outputDir;              
        private Integer codepage;
        private Integer encodingType;
        private List<String[]> copyrightMsgs = new ArrayList<String[]>();
        private List<String[]> licenseInfos = new ArrayList<String[]>();


        public OverviewBuilder(OverviewMap overviewSource) {
                this.overviewSource = overviewSource;
        }

        public void init(CommandArgs args) {
                areaName = args.get("area-name", "Overview Map");
                overviewMapname = args.get("overview-mapname", "osmmap");
                overviewMapnumber = args.get("overview-mapnumber", "63240000");
                outputDir = args.getOutputDir();
        }

        public void onMapEnd(FileInfo finfo) {
                if (!finfo.isImg())
                        return;

                try {
                        readFileIntoOverview(finfo);
                } catch (FileNotFoundException e) {
                        throw new MapFailedException("Could not read detail map " + finfo.getFilename(), e);
                }
        }

        public void onFinish() {
                addBackground();
                writeOverviewMap();
        }
       
        /**
         * Add background polygon that covers the whole area of the overview map.
         */

        private void addBackground() {
                MapShape background = new MapShape();
                background.setType(0x4b); // background type
                background.setMinResolution(0); // On all levels
                background.setPoints(overviewSource.getBounds().toCoords());

                overviewSource.addShape(background);
        }

        /**
         * Write out the overview map.
         */

        private void writeOverviewMap() {
                if (overviewSource.mapLevels() == null)
                        return;
                MapBuilder mb = new MapBuilder();
                mb.setEnableLineCleanFilters(false);

                FileSystemParam params = new FileSystemParam();
                params.setBlockSize(512);
                params.setMapDescription(areaName);
                mb.setCopyrights(creMsgList(copyrightMsgs));
                mb.setMapInfo(creMsgList(licenseInfos));
               
               
                try {
                        if (codepage == null){
                                codepage = 0; // should not happen
                        }
                        Sort sort = SrtTextReader.sortForCodepage(codepage);
                        Map map = Map.createMap(overviewMapname, outputDir, params, overviewMapnumber, sort);
                       
                        if (encodingType != null){
                                map.getLblFile().setEncoder(encodingType, codepage);
                        }
                        mb.makeMap(map, overviewSource);
                        map.close();
                } catch (FileExistsException e) {
                        throw new ExitException("Could not create overview map", e);
                } catch (FileNotWritableException e) {
                        throw new ExitException("Could not write to overview map", e);
                }
        }

        /**
         * Add an individual .img file to the overview map.
         *
         * @param finfo Information about an individual map.
         */

        private void readFileIntoOverview(FileInfo finfo) throws FileNotFoundException {
                addMapCoverageArea(finfo);

                MapReader mapReader = null;
                String filename = finfo.getFilename();
                if (codepage == null){
                        codepage = finfo.getCodePage();
                }
                if (codepage != finfo.getCodePage()){
                        System.err.println("WARNING: input file " + filename + " has different code page " + finfo.getCodePage());
                }

                try{
                        mapReader = new MapReader(filename);

                        if (encodingType == null){
                                encodingType = mapReader.getEncodingType();
                        }
                        if (encodingType != mapReader.getEncodingType()){
                                System.err.println("WARNING: input file " + filename + " has different charset type " + encodingType);
                        }

                        String[] msgs = mapReader.getCopyrights();
                        boolean found = false;
                        for (String[] block : copyrightMsgs) {
                                if (Arrays.deepEquals(block, msgs)){
                                        found = true;
                                        break;
                                }
                        }
                        if (!found )
                                copyrightMsgs.add(msgs);
                       
                        msgs = finfo.getLicenseInfo();
                        found = false;
                        for (String[] block : licenseInfos) {
                                if (Arrays.deepEquals(block, msgs)){
                                        found = true;
                                        break;
                                }
                        }
                        if (!found )
                                licenseInfos.add(msgs);
                       
                       
                        levels = mapReader.getLevels();
                        if (overviewSource.mapLevels() == null){
                                LevelInfo[] mapLevels;
                                if (isOverviewImg(filename)){
                                        mapLevels = new LevelInfo[levels.length-1];
                                        for (int i = 1; i < levels.length; i++){
                                                mapLevels[i-1] = new LevelInfo(levels[i].getLevel(), levels[i].getResolution());
                                        }
                                } else {
                                        mapLevels = new LevelInfo[1];
                                        mapLevels[0] = new LevelInfo(levels[1].getLevel(), levels[1].getResolution());
                                }
                                overviewSource.setMapLevels(mapLevels);
                        }
                        if (isOverviewImg(filename)){
                                readPoints(mapReader);
                                readLines(mapReader);
                                readShapes(mapReader);
                        }
                } catch (FileNotFoundException e) {
                        throw new ExitException("Could not open " + filename + " when creating overview file");
                } finally {
                        Utils.closeFile(mapReader);
                }
        }

        /**
         * Read the points from the .img file and add them to the overview map.
         * We read from the least detailed level (apart from the empty one).
         *
         * @param mapReader Map reader on the detailed .img file.
         */

        private void readPoints(MapReader mapReader) {
                Area bounds = overviewSource.getBounds();
                for (int l = 1; l < levels.length; l++){
                        int min = levels[l].getLevel();
                        int res = levels[l].getResolution();
                        List<Point> pointList = mapReader.pointsForLevel(min, MapReader.WITH_EXT_TYPE_DATA);
                        for (Point point: pointList) {
                                if (log.isDebugEnabled())
                                        log.debug("got point", point);
                                if (bounds.contains(point.getLocation()) == false){
                                        if (log.isDebugEnabled())
                                                log.debug(point, "dropped, is outside of tile boundary");
                                        continue;
                                }
                                MapPoint mp = new MapPoint();
                                mp.setType(point.getType());
                                mp.setName(point.getLabel().getText());
                                mp.setMaxResolution(res);
                                mp.setMinResolution(res);  
                                mp.setLocation(point.getLocation());
                                overviewSource.addPoint(mp);
                        }
                }
        }

        /**
         * Read the lines from the .img file and add them to the overview map.
         * We read from the least detailed level (apart from the empty one).
         *
         * @param mapReader Map reader on the detailed .img file.
         */

        private void readLines(MapReader mapReader) {
                for (int l = 1; l < levels.length; l++){
                        int min = levels[l].getLevel();
                        int res = levels[l].getResolution();
                        List<Polyline> lineList = mapReader.linesForLevel(min);
                        //System.out.println(lineList.size() + " lines in lowest resolution " + levels[1].getResolution());
                        for (Polyline line : lineList) {
                                if (log.isDebugEnabled())
                                        log.debug("got line", line);
                                MapLine ml = new MapLine();

                                List<Coord> points = line.getPoints();
                                if (log.isDebugEnabled())                      
                                        log.debug("line point list", points);
                                if (points.size() < 2)
                                        continue;

                                ml.setType(line.getType());
                                if (line.getLabel() != null)
                                        ml.setName(line.getLabel().getText());
                                ml.setMaxResolution(res);
                                ml.setMinResolution(res);  
                                ml.setPoints(points);

                                overviewSource.addLine(ml);
                        }
                }
        }

        /**
         * Read the polygons from the .img file and add them to the overview map.
         * We read from the least detailed level (apart from the empty one).
         *
         * @param mapReader Map reader on the detailed .img file.
         */

        private void readShapes(MapReader mapReader) {
                for (int l = 1; l < levels.length; l++){
                        int min = levels[l].getLevel();
                        int res = levels[l].getResolution();
                        List<Polygon> list = mapReader.shapesForLevel(min);
                        for (Polygon shape : list) {
                                if (log.isDebugEnabled())
                                        log.debug("got polygon", shape);
                                if (shape.getType() == 0x4b){
                                        // ignore existing background polygons as we will add our own
                                        continue;
                                }
                                MapShape ms = new MapShape();

                                List<Coord> points = shape.getPoints();
                                if (log.isDebugEnabled())                      
                                        log.debug("polygon point list", points);

                                if (points.size() < 3)
                                        continue;

                                ms.setType(shape.getType());
                                if (shape.getLabel() != null)
                                        ms.setName(shape.getLabel().getText());
                                ms.setMaxResolution(res);
                                ms.setMinResolution(res);  
                                ms.setPoints(points);

                                overviewSource.addShape(ms);
                        }
                }
        }

        /**
         * Add an area that shows the area covered by a detailed map.  This can
         * be an arbitary shape, although at the current time we only support
         * rectangles.
         *
         * @param finfo Information about a detail map.
         */

        private void addMapCoverageArea(FileInfo finfo) {
                Area bounds = finfo.getBounds();
                List<Coord> points = bounds.toCoords();
               
                for (Coord co: points){
                        overviewSource.addToBounds(co);
                }
                // Create the background rectangle
                MapShape bg = new MapShape();
                bg.setType(0x4a);
                bg.setPoints(points);
                bg.setMinResolution(0);
                bg.setName(finfo.getDescription() + '\u001d' + finfo.getMapname());

                overviewSource.addShape(bg);
        }

        public Area getBounds() {
                return overviewSource.getBounds();
        }

        /**
         * Check if the the file name points to a partly overview img file  
         * @param name full path or just a name
         * @return true if the name points to a partly overview img file
         */

        public static boolean isOverviewImg (String name){
                return new File(name).getName().startsWith(OVERVIEW_PREFIX);
        }
        /**
         * Add the prefix to the file name.
         * @param name filename
         * @return filename of the corresponding overview img file
         */

        public static String getOverviewImgName (String name){
                File f = new File(name);
                return new File(f.getParent(),OverviewBuilder.OVERVIEW_PREFIX + f.getName()).getAbsolutePath();
        }

        public static String getMapName(String name) {
                String fname = new File(name).getName();
                if (fname.startsWith(OVERVIEW_PREFIX))
                        return fname.substring(OVERVIEW_PREFIX.length());
                else return name;
        }
       
        private List<String> creMsgList(List<String[]> msgs){
                ArrayList< String> list = new ArrayList<String>();
                for (int i = 0; i < msgs.size(); i++){
                        String[] block = msgs.get(i);
                        for (String s : block){
                                list.add(s);
                        }
                        if (i < msgs.size()-1){
                                // separate blocks
                                list.add("");
                        }
                }
                return list;
        }
}