Subversion Repositories mkgmap

Rev

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

/*
 * Copyright (C) 2017.
 *
 * 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.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import uk.me.parabola.imgfmt.app.Coord;
import uk.me.parabola.log.Logger;
import uk.me.parabola.mkgmap.osmstyle.NameFinder;
import uk.me.parabola.mkgmap.reader.osm.boundary.Boundary;
import uk.me.parabola.mkgmap.reader.osm.boundary.BoundaryQuadTree;
import uk.me.parabola.util.EnhancedProperties;
import uk.me.parabola.util.Java2DConverter;

/**
 * Hook to add tag mkgmap:residential to elements that lie within a landuse=residential area.
 * @author Gerd Petermann
 *
 */

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

       
        private BoundaryQuadTree residentialBoundaries;
       
        private ElementSaver saver;
        private NameFinder nameFinder;

        @Override
        public boolean init(ElementSaver saver, EnhancedProperties props, Style style) {
                if (!props.getProperty("residential-hook", true))
                        return false;
                this.nameFinder = new NameFinder(props);
                this.saver = saver;
                return true;
        }

        @Override
        public void end() {
                log.info("Starting with residential hook");

                long t1 = System.currentTimeMillis();
                residentialBoundaries = buildResidentialBoundaryTree();
                long t2 = System.currentTimeMillis();
                log.info("Creating residential bounds took", (t2 - t1), "ms");
               
                // process all nodes that might be converted to a garmin node (tagcount > 0)
                for (Node node : saver.getNodes().values()) {
                        if (node.getTagCount() > 0 && saver.getBoundingBox().contains(node.getLocation())) {
                                processElem(node);
                        }
                }

                // process  all ways that might be converted to a garmin way (tagcount > 0)
                for (Way way : saver.getWays().values()) {
                        if (way.getTagCount() > 0) {
                                processElem(way);
                        }
                }
                long t3 = System.currentTimeMillis();
                log.info("Using residential bounds took", (t3 - t2), "ms");
                // free memory
                residentialBoundaries = null;
        }

        private static final short TK_LANDUSE = TagDict.getInstance().xlate("landuse");
        private static final short TK_NAME = TagDict.getInstance().xlate("name");  
        private static final short TKM_STYLEFILTER = TagDict.getInstance().xlate("mkgmap:stylefilter");
        private static final short TKM_OTHER = TagDict.getInstance().xlate("mkgmap:other");
       
        private BoundaryQuadTree buildResidentialBoundaryTree() {
                List<Boundary> residentials = new ArrayList<>();
                Tags tags = new Tags();
               
                for (Way way : saver.getWays().values()) {
                        if (way.hasIdenticalEndPoints() && "residential".equals(way.getTag(TK_LANDUSE))) {
                                if ("polyline".equals(way.getTag(TKM_STYLEFILTER)))
                                        continue;
                                String name = nameFinder.getName(way);
                                tags.put(TK_NAME, name == null ? "yes": name);
                                Boundary b = new Boundary(Java2DConverter.createArea(way.getPoints()), tags, "w"+way.getId());
                                residentials.add(b);
                        }
                }
               
                return new BoundaryQuadTree(saver.getBoundingBox(), residentials, null);
        }

        /**
         * Extract the location info and perform a test
         * against the BoundaryQuadTree. If found, assign the tag.
         * @param elem A way or Node
         */

        private void processElem(Element elem){
                Tags residentialTags = null;

                if (elem instanceof Node){
                        Node node = (Node) elem;
                        residentialTags = residentialBoundaries.get(node.getLocation());
                } else if (elem instanceof Way){
                        Way way = (Way) elem;
                        // try the mid point of the way first
                        int middle = way.getPoints().size() / 2;
                        Coord loc = way.hasIdenticalEndPoints() ? way.getCofG() : way.getPoints().get(middle);
                        if (! "residential".equals(way.getTag(TK_LANDUSE)))
                                residentialTags = residentialBoundaries.get(loc);
                }

                if (residentialTags != null) {
                        elem.addTag("mkgmap:residential", residentialTags.get(TKM_OTHER));
                }
        }
       
        @Override
        public Set<String> getUsedTags() {
                return new HashSet<>(Arrays.asList("landuse", "name"));
        }
}