Subversion Repositories mkgmap

Rev

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

/*
 * Copyright (C) 2006 - 2012.
 *
 * 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.reader.osm;

import java.util.HashMap;
import java.util.Map;
import java.util.Set;

import uk.me.parabola.imgfmt.app.Area;
import uk.me.parabola.imgfmt.app.Coord;

/**
 * Base class for OSM file handlers.
 *
 * @author Steve Ratcliffe
 */

public class OsmHandler {
        // Elements that are read are saved/further processed by these two classes.
        protected ElementSaver saver;
        protected OsmReadingHooks hooks;

        private final Map<String, Long> fakeIdMap = new HashMap<String, Long>();
        private Map<String,Set<String>> deletedTags;
        private Map<String, String> usedTags;

        // Node references within a way
        protected long firstNodeRef;
        protected long lastNodeRef;
        protected boolean missingNodeRef;

        /**
         * Tag that is set to <code>true</code> if one or more tags are not loaded.
         * Only used for multipolygons yet.
         */

        public static final String TAGS_INCOMPLETE_TAG = "mkgmap:tagsincomplete";
       
        /**
         * Set a set of tags with values that are to be deleted on input.
         * For each key there is a set of values.  If the value set is empty then
         * all tags with the given key are deleted.  If the value set is not empty
         * then only tags with the given key that has one of the given values are
         * deleted.
         *
         * @param deletedTags A map of tag key, to a set of values to be deleted.
         */

        public void setTagsToDelete(Map<String, Set<String>> deletedTags) {
                this.deletedTags = deletedTags;
        }

        /**
         * This sets a list of all the tags that are used in the system.
         *
         * Assuming this list is complete, no other tag can have an effect on the output
         * and can therefore be dropped on input. This reduces memory usage, sometimes
         * dramatically if there are many useless tags in the input.
         *
         * We keep a map of tag-name to tag-name.  This allows us to keep only a single
         * copy of each string.  This also results in a reasonable reduction in memory usage.
         *
         * @param used The complete set of tags that are used to form the output.
         */

        public void setUsedTags(Set<String> used) {
                if (used == null || used.isEmpty()) {
                        usedTags = null;
                        return;
                }
                usedTags = new HashMap<String, String>();
                for (String s : used) {
                        if (s == null) {
                                continue;
                        }
                        // intern the keys
                        s = s.intern();
                        usedTags.put(s, s);
                }
        }

        /**
         * Some tags are dropped at the input stage.  We drop tags that are not going
         * to be used and there is also an option to provide a file containing tags to
         * be dropped.
         *
         * @param key The tag key.
         * @param val The tag value.
         * @return Returns the tag key if this tag should be kept.  Returns null if the tag
         * should be discarded.
         */

        protected String keepTag(String key, String val) {
                if(deletedTags != null) {
                        Set<String> vals = deletedTags.get(key);
                        if(vals != null && (vals.isEmpty() || vals.contains(val))) {
                                return null;
                        }
                }

                // By returning the value stored in usedTags, instead of the key, we ensure
                // that the same string is always used so saving some memory.
                if (usedTags != null)
                        return usedTags.get(key);

                return key;
        }

        /**
         * Actually set the bounding box.  The boundary values are given.
         */

        protected void setBBox(double minlat, double minlong, double maxlat, double maxlong) {
                Area bbox = new Area(minlat, minlong, maxlat, maxlong);
                saver.setBoundingBox(bbox);
        }

        /**
         * Convert an id as a string to a number. If the id is not a number, then create
         * a unique number instead.
         * @param id The id as a string. Does not have to be a numeric quantity.
         * @return A long id, either parsed from the input, or a unique id generated internally.
         */

        protected long idVal(String id) {
                try {
                        // attempt to parse id as a number
                        return Long.parseLong(id);
                } catch (NumberFormatException e) {
                        // if that fails, fake a (hopefully) unique value
                        Long fakeIdVal = fakeIdMap.get(id);
                        if(fakeIdVal == null) {
                                fakeIdVal = FakeIdGenerator.makeFakeId();
                                fakeIdMap.put(id, fakeIdVal);
                        }
                        //System.out.printf("%s = 0x%016x\n", id, fakeIdVal);
                        return fakeIdVal;
                }
        }

        public void setElementSaver(ElementSaver elementSaver) {
                this.saver = elementSaver;
        }

        public void setHooks(OsmReadingHooks plugin) {
                this.hooks = plugin;
        }

        /**
         * Common actions to take when creating a new way.
         * Reset some state and create the Way object.
         * @param id The osm id of the new way.
         * @return The new Way itself.
         */

        protected Way startWay(long id) {
                firstNodeRef = 0;
                lastNodeRef = 0;
                missingNodeRef = false;
                return new Way(id);
        }

        /**
         * Common actions to take when a way has been completely read by the parser.
         * It is saved
         * @param way The way that was read.
         */

        protected void endWay(Way way) {
                way.setClosedInOSM(firstNodeRef == lastNodeRef);
                way.setComplete(!missingNodeRef);

                saver.addWay(way);
                hooks.onAddWay(way);
        }

        /**
         * Add a coordinate point to the way.
         * @param way The Way.
         * @param id The coordinate id.
         */

        protected void addCoordToWay(Way way, long id) {
                lastNodeRef = id;
                if (firstNodeRef == 0) firstNodeRef = id;

                Coord co = saver.getCoord(id);

                if (co != null) {
                        hooks.onCoordAddedToWay(way, id, co);
                        co = saver.getCoord(id);
                        way.addPoint(co);
                } else {
                        missingNodeRef = true;
                }
        }
}