/* * 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.File; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; import java.io.InputStream; import java.nio.charset.Charset; import java.nio.file.Files; 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.ExitException; 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(), new HousenumberHooks(), }; protected OsmConverter converter; private final Set<String> usedTags = new HashSet<>(); 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 copyrightFileName = getConfig().getProperty("copyright-file", null); if (copyrightFileName != null) { List<String> copyrightArray = new ArrayList<>(); try { File file = new File(copyrightFileName); BufferedReader reader = Files.newBufferedReader(file.toPath(), Charset.forName("utf-8")); String text; while ((text = reader.readLine()) != null) { copyrightArray.add(text); } reader.close(); } catch (FileNotFoundException e) { throw new ExitException("Could not open copyright file " + copyrightFileName); } catch (IOException e) { throw new ExitException("Error reading copyright file " + copyrightFileName); } String[] copyright = new String[copyrightArray.size()]; copyrightArray.toArray(copyright); return copyright; } 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(); } }