Subversion Repositories mkgmap

Rev

Rev 4507 | View as "text/plain" | Blame | Compare with Previous | Last modification | View Log | RSS feed

/*
 * Copyright (C) 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.awt.Rectangle;
import java.util.ArrayList;
import java.util.List;
import java.util.Map.Entry;

import uk.me.parabola.imgfmt.app.Area;
import uk.me.parabola.imgfmt.app.Coord;
import uk.me.parabola.log.Logger;
import uk.me.parabola.util.EnhancedProperties;

/**
 * The hook removes all elements that will not be included in the map and can therefore
 * be safely removed. This improves the performance because the elements do not have
 * to go through the style system.
 *
 * @author WanMil
 */

public class UnusedElementsRemoverHook implements OsmReadingHooks {
        private static final Logger log = Logger.getLogger(UnusedElementsRemoverHook.class);

        private ElementSaver saver;
       
        /** node with tags of this list must not be removed */
        private List<Entry<String,String>> areasToPoiNodeTags;
       
        @Override
        public boolean init(ElementSaver saver, EnhancedProperties props, Style style) {
                this.saver = saver;
               
                // Get the tags from the POIGeneratorHook which are used to define the point
                // where the POI is placed in polygons. They must not be removed if the polygon
                // is not removed. Checking if the polygon is not removed is too costly therefore
                // all nodes with these tags are kept.
                if (props.containsKey("add-pois-to-areas"))
                        areasToPoiNodeTags = POIGeneratorHook.getPoiPlacementTags(props);
                else
                        areasToPoiNodeTags = new ArrayList<>();
                return true;
        }
       
        @Override
        public void end() {
                long t1 = System.currentTimeMillis();
                log.info("Removing unused elements");
               
                final Area bbox = saver.getBoundingBox();
               
                long nodes = saver.getNodes().size();

                // go through all nodes
                for (Node node : new ArrayList<>(saver.getNodes().values())) {

                        // nodes without tags can be removed
                        if (node.getTagCount() == 0) {
                                saver.getNodes().remove(node.getId());
                                continue;
                        }
                       
                        // check if the node is within the tile bounding box
                        if (!bbox.contains(node.getLocation())) {
                                boolean removeNode = true;
                               
                                for (Entry<String, String> tag : areasToPoiNodeTags) {
                                        // check if the node has a tag used by the POIGeneratorHook
                                        String val = node.getTag(tag.getKey());
                                        if (val != null && (tag.getValue() == null || val.equals(tag.getValue()))) {
                                                // the node contains one tag that might be
                                                // interesting for the POIGeneratorHook
                                                // do not remove it
                                                removeNode = false;
                                                break;
                                        }
                                }
                                if (removeNode) {
                                        saver.getNodes().remove(node.getId());
                                } else {
                                        log.debug("Keep node", node, "because it contains a tag which might be required for the area-to-poi function.");
                                }
                        }
                }
               
                Rectangle bboxRect = new Rectangle(bbox.getMinLong(), bbox.getMinLat(), bbox.getWidth(), bbox.getHeight());
                long ways = saver.getWays().size();
                for (Way way : new ArrayList<>(saver.getWays().values())) {
                        if (way.isViaWay())
                                continue;
                        if (way.getPoints().isEmpty()) {
                                // empty way will not appear in the map => remove it
                                saver.getWays().remove(way.getId());
                                continue;
                        }
                       
                        // check if a way has no tags
                        // it is presumed that the RelationStyleHook was already executed
                        if (way.getTagCount() == 0) {
                                saver.getWays().remove(way.getId());
                                continue;
                        }
                       
                        // check if the way is completely outside the tile bounding box
                        boolean coordInBbox = false;
                        Coord prevC = null;
                       
                        // It is possible that the way is larger than the bounding box and therefore
                        // contains the bbox completely. Especially this is true for the sea polygon
                        // when using --generate-sea=polygon
                        for (Coord c : way.getPoints()) {
                                if (bbox.contains(c)) {
                                        coordInBbox = true;
                                        break;
                                } else if (prevC != null &&
                                                bboxRect.intersectsLine(prevC.getLongitude(), prevC.getLatitude(), c.getLongitude(), c.getLatitude())) {
                                        // the line intersects the bounding box
                                        if (log.isDebugEnabled()) {
                                                log.debug("Intersection!");
                                                log.debug("Bbox:", bbox);
                                                log.debug("Way coords:", prevC, c);
                                        }
                                        coordInBbox = true;
                                        break;
                                }
                               
                                prevC = c;
                        }
                        if (!coordInBbox) {
                                // no coord of the way is within the bounding box
                                // check if the way possibly covers the bounding box completely
                                Area wayBbox = Area.getBBox(way.getPoints());
                                if (wayBbox.contains(saver.getBoundingBox())) {
                                        log.debug(way, "possibly covers the bbox completely. Keep it.");
                                } else {
                                        saver.getWays().remove(way.getId());
                                }
                        }
                }
               
                log.info("Nodes: before:", nodes, "after:", saver.getNodes().size());  
                log.info("Ways: before:", ways, "after:", saver.getWays().size());     
                log.info("Removing unused elements took", (System.currentTimeMillis()-t1), "ms");
        }
}