Subversion Repositories mkgmap

Rev

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

/*
 * Copyright (C) 2008 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: 04-Aug-2008
 */

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

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;

import uk.me.parabola.imgfmt.app.Coord;
import uk.me.parabola.imgfmt.app.CoordNode;
import uk.me.parabola.imgfmt.app.net.AccessTagsAndBits;
import uk.me.parabola.imgfmt.app.net.NumberStyle;
import uk.me.parabola.imgfmt.app.net.Numbers;
import uk.me.parabola.log.Logger;
import uk.me.parabola.mkgmap.general.MapLine;
import uk.me.parabola.mkgmap.general.MapRoad;

/**
 * Used to remember all the road relevant parameters in a definition which
 * can occur in any order. Also remembers routing nodes and makes sure
 * the generated MapRoads all have the same RoutingNode objects.
 *
 * Use one instance of RoadHelper per file, and reset after reading
 * each road.
 */

class RoadHelper {
        private static final Logger log = Logger.getLogger(RoadHelper.class);

        // routing node store, persistent over resets
        private final Map<Long, CoordNode> nodeCoords = new HashMap<>();

        // Next node number to use for nodes constructed for house numbers. Persists over reset.
        private long houseNumberNodeNumber = 16000000;

        private int roadId;
        private final List<NodeIndex> nodes = new ArrayList<>();

        private int speed;
        private int roadClass;

        private boolean oneway;
        private boolean toll;

        private byte mkgmapAccess;
        private List<Numbers> numbers;

        public RoadHelper() {
                clear();
        }

        public void clear() {
                roadId = 0;
                nodes.clear();

                speed = 0;
                roadClass = 0;
                oneway = false;
                toll = false;
                numbers = null;
        }

        public void setRoadId(int roadId) {
                this.roadId = roadId;
        }

        public void addNode(String value) {
                String[] f = value.split(",");
                nodes.add(new NodeIndex(f));
        }

        /**
         * @param param cgpsmapper manual:
         * RouteParam=speed,road_class,one_way,toll,
         * denied_emergency,denied_delivery,denied_car,denied_bus,denied_taxi,denied_pedestrain,denied_bicycle,denied_truck
         */

        public void setParam(String param) {
                String[] f = param.split(",");
                speed = Integer.parseInt(f[0]);
                if (speed < 0)
                        speed = 0;
                if (speed > 7)
                        speed = 7;
                roadClass = Integer.parseInt(f[1]);
                if (roadClass < 0)
                        roadClass = 0;
                if (roadClass > 4)
                        roadClass = 4;
                oneway = Integer.parseInt(f[2]) > 0;
                toll = Integer.parseInt(f[3]) > 0;
                byte noAccess = 0;
                for (int j = 0; j < f.length - 4; j++){
                        if (Integer.parseInt(f[4+j]) == 0)
                                continue;
                        switch (j){
                        case 0: noAccess |= AccessTagsAndBits.EMERGENCY; break;
                        case 1: noAccess |= AccessTagsAndBits.DELIVERY; break;
                        case 2: noAccess |= AccessTagsAndBits.CAR; break;
                        case 3: noAccess |= AccessTagsAndBits.BUS; break;
                        case 4: noAccess |= AccessTagsAndBits.TAXI; break;
                        case 5: noAccess |= AccessTagsAndBits.FOOT; break;
                        case 6: noAccess |= AccessTagsAndBits.BIKE; break;
                        case 7: noAccess |= AccessTagsAndBits.TRUCK; break;
                        }
                }
                mkgmapAccess = (byte) ~noAccess; // we store the allowed vehicles
        }

        public MapRoad makeRoad(MapLine l) {
                assert roadId != 0;

                if (log.isDebugEnabled())
                        log.debug("finishing road id " + roadId);

                MapRoad road = new MapRoad(roadId, l);

                // Set parameters.
                road.setRoadClass(roadClass);
                road.setSpeed(speed);
                if (oneway)
                        road.setOneway();
                if (toll)
                        road.setToll();
                road.setAccess(mkgmapAccess);

                if (numbers != null && !numbers.isEmpty()) {
                        convertNodesForHouseNumbers();
                        road.setNumbers(numbers);
                }

                List<Coord> points = road.getPoints();
                road.setNumNodes(nodes.size());

                boolean starts = false;
                boolean intern = false;
                for (NodeIndex ni : nodes) {
                        int n = ni.index;
                        if (n == 0)
                                starts = true;
                        else if (n < points.size() - 1)
                                intern = true;
                        if (log.isDebugEnabled())
                                log.debug("road has " + points.size() +" points");
                        Coord coord = points.get(n);
                        long id = coord.getId();
                        if (id == 0) {
                                CoordNode node = nodeCoords.get((long) ni.nodeId);
                                if (node == null) {
                                        node = new CoordNode(coord, ni.nodeId, ni.boundary);
                                        nodeCoords.put((long) ni.nodeId, node);
                                }
                                points.set(n, node);
                        } else if (id != ni.nodeId) {
                                log.warn("Inconsistant node ids");
                        }
                }
                road.setStartsWithNode(starts);
                road.setInternalNodes(intern);

                return road;
        }

        /**
         * Convert the node index into a routing node number.
         *
         * If necessary a new routing node is created, if there is not one already
         * These constructed routing nodes are not connected to any other road and so
         * should be marked as such in the NOD2 bit stream, but we don't appear to do that yet.
         *
         * Only called if numbers is non-null and not empty.
         */

        private void convertNodesForHouseNumbers() {
                for (Numbers n : numbers) {
                        int node = n.getNodeNumber();

                        // This assumes that the nodes are sorted by index.
                        ListIterator<NodeIndex> iterator = nodes.listIterator();
                        while (iterator.hasNext()) {
                                NodeIndex ni = iterator.next();
                                if (ni.index == node) {
                                        // It was already there (a common case)
                                        n.setRnodNumber(iterator.previousIndex());
                                        break;
                                } else if (ni.index > node) {
                                        // there is no routing node for this node index, need to insert one.
                                        break;
                                }
                        }

                        // If we don't have a routing node number then we have to construct one.
                        if (!n.hasRnodNumber()) {
                                NodeIndex hnNode = new NodeIndex(new String[] {
                                                String.valueOf(node),
                                                String.valueOf(houseNumberNodeNumber++),
                                                "0"
                                });

                                iterator.previous();
                                iterator.add(hnNode);
                                n.setRnodNumber(iterator.previousIndex());
                                //System.out.printf("ADDING RN on %d, hn=%s, rn=%d\n", roadId, hnNode, n.getRnodNumber());
                        }
                }

                // Sanity checking. TODO remove
                //int lastInd = -1;
                //for (NodeIndex n : nodes) {
                //      assert n.index > lastInd;
                //      lastInd = n.index;
                //
                //}
                //System.out.println("start");
                //Numbers num = null;
                //for (Numbers n1 : numbers) {
                //      int ncount = 0;
                //      for (NodeIndex n : nodes) {
                //              System.out.printf("n1.node=%d, ni=%s, ni.index=%d\n", n1.getNodeNumber(), n, n.index);
                //              if (n1.getNodeNumber() == n.index) {
                //                      num = n1;
                //                      break;
                //              }
                //              ncount++;
                //      }
                //      assert num != null && num.getRnodNumber() == ncount;
                //}
        }

        public boolean isRoad() {
                return roadId != 0;
        }

        public Map<Long, CoordNode> getNodeCoords() {
                return nodeCoords;
        }

        public void addNumbers(String value) {
                if (numbers == null)
                        numbers = new ArrayList<>();
                Numbers num = new Numbers(value);
                if (num.getLeftNumberStyle() != NumberStyle.NONE || num.getRightNumberStyle() != NumberStyle.NONE)
                        numbers.add(num);
        }

        private static class NodeIndex {
                private final int index;
                private final int nodeId;
                private boolean boundary;

                private NodeIndex(String[] f) {
                        // f[0] is the index into the line
                        // f[1] is the node id
                        // f[2] is whether it's a boundary node
                        index = Integer.parseInt(f[0]);
                        nodeId = Integer.parseInt(f[1]);
                        if (f.length > 2)
                                boundary = Integer.parseInt(f[2]) > 0;
                        if (log.isDebugEnabled())
                                log.debug("ind=" + index + "node=" + nodeId + "bound=" + boundary);
                }

                public String toString() {
                        return String.format("%d,%d,%b", index, nodeId, boundary);
                }
        }
}