Subversion Repositories mkgmap

Rev

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

/*
 * Copyright (C) 2007 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: 22-Sep-2007
 */

package uk.me.parabola.mkgmap.reader.osm;

import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import uk.me.parabola.imgfmt.FormatException;
import uk.me.parabola.imgfmt.Utils;
import uk.me.parabola.log.Logger;
import uk.me.parabola.mkgmap.general.LevelInfo;
import uk.me.parabola.mkgmap.general.LoadableMapDataSource;
import uk.me.parabola.mkgmap.osmstyle.StyleImpl;
import uk.me.parabola.mkgmap.osmstyle.StyledConverter;
import uk.me.parabola.mkgmap.reader.MapperBasedMapDataSource;
import uk.me.parabola.util.EnhancedProperties;

/**
 * Base class for OSM map sources.  It exists so that more than
 * one version of the api can be supported at a time.
 *
 * @author Steve Ratcliffe
 */

public abstract class OsmMapDataSource extends MapperBasedMapDataSource
                implements LoadableMapDataSource, LoadableOsmDataSource
{
        private static final Logger log = Logger.getLogger(OsmMapDataSource.class);

        private Style style;
        private final OsmReadingHooks[] POSSIBLE_HOOKS = {
                        new SeaGenerator(),
                        new MultiPolygonFinishHook(),
                        new RelationStyleHook(),
                        new LinkDestinationHook(),
                        new UnusedElementsRemoverHook(),
                        new RoutingHook(),
                        new HighwayHooks(),
                        new LocationHook(),
                        new POIGeneratorHook(),
        };
        protected OsmConverter converter;
        private final Set<String> usedTags = new HashSet<String>();
        protected ElementSaver elementSaver;
        protected OsmReadingHooks osmReadingHooks;

        /**
         * Get the maps levels to be used for the current map.  This can be
         * specified in a number of ways in order:
         * <ol>
         * <li>On the command line with the --levels flag.
         * The format is a comma (or space) separated list of level/resolution
         * pairs.  Eg --levels=0:24,1:22,2:20
         * If the flag is given without an argument then the command line override
         * is turned off for maps following that option.
         *
         * <li>In the style options file.  This works just like the command line
         * option, but it applies whenever the given style is used and not overridden
         * on the command line.
         *
         * <li>A default setting.
         * </ol>
         *
         * <p>I'd advise that new styles specify their own set of levels.
         *
         * @return An array of level information, basically a [level,resolution]
         * pair.
         */

        public LevelInfo[] mapLevels() {
                String levelSpec = getLevelSpec("levels");
                if (levelSpec == null)
                        levelSpec = LevelInfo.DEFAULT_LEVELS;

                return LevelInfo.createFromString(levelSpec);
        }

        @Override
        public LevelInfo[] overviewMapLevels() {
                String levelSpec = getLevelSpec("overview-levels");
               
                if (levelSpec == null)
                        return null;
                LevelInfo[] levels = LevelInfo.createFromString(levelSpec);
                for (int i = 0; i < levels.length; i++)
                        levels[i] = new LevelInfo(levels.length-i-1,levels[i].getBits());
                return levels;
        }
               
        private String getLevelSpec (String optionName){
                // First try command line, then style, then our default.
                String levelSpec = getConfig().getProperty(optionName);
                log.debug(optionName, levelSpec, ", ", ((levelSpec!=null)?levelSpec.length():""));
                if (levelSpec == null || levelSpec.length() < 2) {
                        if (style != null) {
                                levelSpec = style.getOption(optionName);
                                log.debug("getting " + optionName + " from style:", levelSpec);
                        }
                }
                return levelSpec;
        }
       
        @Override
        public void load(String name) throws FileNotFoundException, FormatException {
                InputStream is = Utils.openFile(name);
                load(is);
        }

        /**
         * There are no copyright messages in the OSM files themselves.  So we
         * include a fixed set of strings on the assumption that .osm files
         * are probably going to have the OSM copyright statements.
         *
         * @return A list of copyright messages as a String array.
         */

        public String[] copyrightMessages() {
                String note = getConfig().getProperty("copyright-message",
                                "OpenStreetMap.org contributors. See: http://wiki.openstreetmap.org/index.php/Attribution");
                return new String[] { note };
        }

        protected void setStyle(Style style) {
                this.style = style;
        }

        /**
         * Common code to setup the file handler.
         * @param handler The file handler.
         */

        protected void setupHandler(OsmHandler handler) {
                createElementSaver();
                createConverter();
               
                osmReadingHooks = pluginChain(elementSaver, getConfig());

                handler.setElementSaver(elementSaver);
                handler.setHooks(osmReadingHooks);

                handler.setUsedTags(getUsedTags());

                String deleteTagsFileName = getConfig().getProperty("delete-tags-file");
                if(deleteTagsFileName != null) {
                        Map<String, Set<String>> deltags = readDeleteTagsFile(deleteTagsFileName);
                        handler.setTagsToDelete(deltags);
                }
        }
       
        protected void createElementSaver() {
                elementSaver = new ElementSaver(getConfig());
        }
       
        public ElementSaver getElementSaver() {
                return elementSaver;
        }

        protected OsmReadingHooks[] getPossibleHooks() {
                return this.POSSIBLE_HOOKS;
        }
       
        protected OsmReadingHooks pluginChain(ElementSaver saver, EnhancedProperties props) {
                List<OsmReadingHooks> plugins = new ArrayList<OsmReadingHooks>();
                for (OsmReadingHooks p : getPossibleHooks()) {
                        if (p.init(saver, props)){
                                plugins.add(p);
                                if (p instanceof RelationStyleHook)
                                        ((RelationStyleHook) p).setStyle(style);
                        }
                }

                OsmReadingHooks hooks;
                switch (plugins.size()) {
                case 0:
                        hooks = new OsmReadingHooksAdaptor();
                        break;
                case 1:
                        hooks = plugins.get(0);
                        break;
                default:
                        OsmReadingHooksChain chain = new OsmReadingHooksChain();
                        for (OsmReadingHooks p : plugins) {
                                chain.add(p);
                        }
                        hooks = chain;
                }
                usedTags.addAll(hooks.getUsedTags());
                return hooks;
        }

        private Map<String, Set<String>> readDeleteTagsFile(String fileName) {
                Map<String, Set<String>> deletedTags = new HashMap<String,Set<String>>();
                try {
                        BufferedReader br = new BufferedReader(new FileReader(fileName));
                        String line;
                        while((line = br.readLine()) != null) {
                                line = line.trim();
                                if(line.length() > 0 && !line.startsWith("#") && !line.startsWith(";")) {
                                        String[] parts = line.split("=");
                                        if (parts.length == 2) {
                                                parts[0] = parts[0].trim();
                                                parts[1] = parts[1].trim();
                                                if ("*".equals(parts[1])) {
                                                        deletedTags.put(parts[0], new HashSet<String>());
                                                } else {
                                                        Set<String> vals = deletedTags.get(parts[0]);
                                                        if (vals == null)
                                                                vals = new HashSet<String>();
                                                        vals.add(parts[1]);
                                                        deletedTags.put(parts[0], vals);
                                                }
                                        } else {
                                                log.error("Ignoring bad line in deleted tags file: " + line);
                                        }
                                }
                        }
                        br.close();
                }
                catch(FileNotFoundException e) {
                        log.error("Could not open delete tags file " + fileName);
                }
                catch(IOException e) {
                        log.error("Error reading delete tags file " + fileName);
                }

                if(deletedTags.isEmpty())
                        deletedTags = null;

                return deletedTags;
        }

        /**
         * Create the appropriate converter from osm to garmin styles.
         *
         */

        protected void createConverter() {
                EnhancedProperties props = getConfig();
                Style style = StyleImpl.readStyle(props);
                setStyle(style);

                usedTags.addAll(style.getUsedTags());
                converter = new StyledConverter(style, mapper, props);
        }

        public OsmConverter getConverter() {
                return converter;
        }

        public Set<String> getUsedTags() {
                return usedTags;
        }
       
        @Override
        public Boolean getDriveOnLeft(){
                return converter.getDriveOnLeft();
        }
}