Subversion Repositories display

Rev

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

/*
 * Copyright (C) 2014 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 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 test.svg;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Formatter;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import uk.me.parabola.imgfmt.Utils;
import uk.me.parabola.imgfmt.app.Coord;
import uk.me.parabola.imgfmt.app.trergn.Subdivision;

import org.dom4j.Element;
import test.display.CommonDisplay;
import test.elements.Line;
import test.files.NodFile;
import test.files.RouteArc;
import test.files.RouteCenter;
import test.files.RouteNode;

/**
 * Convert a nod file to svg to show the routing relationships.
 *
 * @author Steve Ratcliffe
 */

public class Route extends CommonDisplay {

        private SvgDoc doc;
        private Element route;
        private Element info;
    private Element roads;
    private Element spokes;

    private NodFile nodFile;

    private final Map<Integer, List<Line>> roadMap = new HashMap<>();
    private final List<Integer> roadIds = new ArrayList<>();

    protected void print() {

        openLbl();
        openTre();
        openNet();
        net.setLabels(lbl);
        net.setCities(lbl.getCities());
        net.setZips(lbl.getZips());
        openRgn();
        rgn.setNetFile(net);

        reader.position(0);
        nodFile = new NodFile(reader);
        List<RouteCenter> routeCenters = nodFile.readNodeCenters();
        nodFile.fixMissingNodes();

        initRoads();

                for (RouteCenter center: routeCenters)
                        findMoreNodes(center);
                for (RouteCenter center: routeCenters)
                        findMoreNodes(center);

        int max = 4;
                int min = max - 3;
                if (routeCenters.size() < max)
            max = routeCenters.size();

        if (max <= min)
            min = 0;
        for (int i = min; i < max; i++) {
            RouteCenter c2 = routeCenters.get(i);
            displayCenter(c2);
        }

        drawRoads();
    }

        /**
     * Go through the nodes we have already and add the linked node if we don't have it.
     *
     * @param center All the nodes we know about.
     */

    private void findMoreNodes(RouteCenter center) {
                List<Integer> needed = new ArrayList<>();

        for (RouteNode node : center.nodes().values()) {
            if (node.isBad())
                continue;

            for (RouteArc arc : node.getArcs()) {
                int off = arc.getLink();
                if (nodFile.getNode(off) == null)
                    needed.add(off);
            }
        }

        for (Integer i : needed) {
            if (nodFile.getNode(i) == null) nodFile.fixNode(i);
        }
        nodFile.resort();
    }

        private void initRoads() {
                Subdivision[] subdivisions = tre.subdivForLevel(0);

                for (Subdivision sub : subdivisions) {
                        List<Line> lines = rgn.linesForSubdiv(sub);

                        for (Line line : lines) {
                                int net1Offset = line.getNet1Offset();

                                // Take this to be a road if it has a NET1 Offset
                                if (net1Offset > 0) {
                                        List<Line> larray = roadMap.get(net1Offset);
                                        if (larray == null) {
                                                larray = new ArrayList<>();
                                                roadMap.put(net1Offset, larray);
                                        }
                                        larray.add(line);
                                }
                        }
                }
        }

    private void drawRoads() {
        Collections.sort(roadIds);
        int lastId = -1;
        for (Integer i : roadIds) {
            if (i == lastId)
                continue;

            lastId = i;

            List<Line> lines = roadMap.get(i);
            if (lines == null) {
                if (i != 0)
                    System.out.printf("No lines for road id %x\n", i);
                continue;
            }

            for (Line line : lines) {
                drawLine(line);
            }
        }
    }

    private void displayCenter(RouteCenter c2) {
        XY xy1 = toXY(c2.getCoord());

        route.addElement("circle")
                .addAttribute("cx", xy1.getX())
                .addAttribute("cy", xy1.getY())
                .addAttribute("r", "8")
                .addAttribute("fill", "none")
                .addAttribute("stroke", "orange")
                .addAttribute("stroke-width", "2");

        for (RouteNode node : c2.nodes().values()) {
            drawRouteNode(node);

            if (node.hasArcs()) {
                for (RouteArc arc : node.getArcs()) {
                    if (arc.isBad())
                        continue;

                    drawArc(arc);
                }
            }
        }
    }

        private void drawRouteNode(RouteNode node) {
                XY xy = toXY(node.getCoord());
                Element circ = route.addElement("circle")
                                .addAttribute("cx", xy.getX())
                                .addAttribute("cy", xy.getY())
                                .addAttribute("r", "5")
                                ;

                XY c = toXY(node.getCenter().getCoord());
                spokes.addElement("line")
                                .addAttribute("x1", xy.getX())
                                .addAttribute("y1", xy.getY())
                                .addAttribute("x2", c.getX())
                                .addAttribute("y2", c.getY())
                                .addAttribute("stroke", "orange")
                                .addAttribute("stroke-width", "0.5")
                ;

                if (node.isBad())
                        circ.addAttribute("fill", "red");
                else if (node.isProblem())
                        circ.addAttribute("fill", "#dd9511");

                Element g = info.addElement("g")
                                .addAttribute("transform",
                                                String.format("translate(%s,%s)", xy.getX(), xy.getY()));

                g.addElement("rect")
                                .addAttribute("width", "50")
                                .addAttribute("height", "10")
                                .addAttribute("opacity", "0.8")
                                .addAttribute("fill", "white")
                                .addAttribute("stroke-width", "0.5")
                                .addAttribute("stroke", "black");

                Element text = g.addElement("text")
                                .addAttribute("x", "1")
                                .addAttribute("y", "5")
                                .addAttribute("font-size", "3px");
                text.addElement("tspan")
                                .addAttribute("sodipodi:role", "line")
                                .setText(String.format("0x%x %s", node.getOffset(), xy.toString()));

                text.addElement("tspan")
                                .addAttribute("sodipodi:role", "line")
                                .setText(String.format("%s, narc=%d", node.flagString(), node.getArcs().size()));
        }

    private void drawArc(RouteArc arc) {
        Integer netOffset = arc.getNetOffset();
            if (netOffset != null)
                    roadIds.add(netOffset);

        String colour = "yellow";
                switch (arc.getIndex()) {
                case 0:
                        colour = "black";
                        break;
                case 1:
                        colour = "blue";
                        break;
                case 2:
                        colour = "green";
                        break;
                }

        RouteNode n1 = arc.getNode();
        RouteNode n2 = nodFile.getNode(arc.getLink());

        if (n2 != null) {
                        // Draw an arrow half way to the target node.
            XY p1 = toXY(n1.getCoord());
            XY p2 = toXY(n2.getCoord());

                        int x2 = p1.getXi() + (p2.getXi() - p1.getXi())/2;
                        int y2 = p1.getYi() + (p2.getYi() - p1.getYi())/2;
            route.addElement("line")
                    .addAttribute("x1", p1.getX())
                    .addAttribute("y1", p1.getY())
                    .addAttribute("x2", String.valueOf(x2))
                    .addAttribute("y2", String.valueOf(y2))
                    .addAttribute("stroke", colour)
                    .addAttribute("stroke-wdith", "1")
                                        .addAttribute("style", "marker-end:url(#Arrow2Send)")
            ;

                        if (arc.getIndex() > 0)
                                return;

                        // Add a marker pointing the direction we think the arc goes in...
                        //markDirection(n1.getCoord(), n1.getCoord().bearingTo(n2.getCoord()), 70, colour, true); // test routine
                        if (arc.hasDirection()) {
                                XY tip = markDirection(n1.getCoord(), arc.getDirection(), 50, colour, true);
                                Element g = route.addElement("g")
                                                .addAttribute("transform",
                                                                String.format("translate(%s,%s)", tip.getX(), tip.getY()));

                                Element arcInfo = g.addElement("rect")
                                                .addAttribute("width", "60")
                                                .addAttribute("height", "6")
                                                .addAttribute("fill", "#dddddd")
                                                .addAttribute("opacity", "0.7");

                                Element text = g.addElement("text")
                                                .addAttribute("x", "1")
                                                .addAttribute("y", "4")
                                                .addAttribute("font-size", "3px")
                                                ;
                                text.addElement("tspan")
                                                .setText(String.format("%d: to %x len=%dm a=%.0fdeg(%2x)", arc.getIndex(),
                                                                arc.getLink(), arc.getDistance(), arc.getDirection(),
                                                                arc.getRawDirection()));

                                //text.addElement("tspan")
                                //              .addAttribute("x", "1")
                                //              .addAttribute("dy", "4px")
                                //              .setText(String.format("%d: to %x len=%dm a=%.0fdeg", arc.getIndex(),
                                //                              arc.getLink(), arc.getDistance(), arc.getDirection()));
                        }
                }

    }

    private void drawLine(Line line) {
        List<Coord> coords = line.getCoords();

        Formatter fmt = new Formatter();
        for (int i = 0; i < coords.size(); i++) {
            Coord c = coords.get(i);
            XY co = toXY(c);
            if (i == 0) {
                fmt.format("M %d %d", co.getXi(), co.getYi());
            } else {
                fmt.format(" L %d %d", co.getXi(), co.getYi());
            }
        }

        String colour;
        switch (line.getType()) {
            case 1: colour = "blue"; break;
            case 2: colour = "green"; break;
            case 3: colour = "red"; break;
            default: colour = "#775599"; break;
        }
        roads.addElement("path")
                .addAttribute("stroke", colour)
                .addAttribute("opacity", "0.3")
                .addAttribute("fill", "none")
                                .addAttribute("style", "marker-end:url(#Arrow2Send);marker-mid:url(#Arrow2Send)")
                                .addAttribute("d", fmt.toString());
    }

        /**
         * Draw a small arrow in the given direction with the given length.
         *
         * The direction is a bearing, ie is has to be projected to the xy coords we are using.
         *
         * @param co Draw from here
         * @param dir Direction (bearing) of arrow.
         * @param len Length in meters.
         * @param colour Colour to draw it in.
         * @param dashed
         */

    private XY markDirection(Coord co, double dir, int len, String colour, boolean dashed) {
        XY xy1 = toXY(co);

        double lat1 = Utils.toRadians(co.getLatitude());
        double lon1 = Utils.toRadians(co.getLongitude());
        double dist = len / 6371000.0;  // convert dist in meters to angular distance in radians
        double brng = dir * (Math.PI / 180);

        double lat2 = Math.asin( Math.sin(lat1)*Math.cos(dist) +
                Math.cos(lat1)*Math.sin(dist)*Math.cos(brng) );
        double lon2 = lon1 + Math.atan2(Math.sin(brng)*Math.sin(dist)*Math.cos(lat1),
                Math.cos(dist)-Math.sin(lat1)*Math.sin(lat2));
        lon2 = (lon2+3*Math.PI) % (2*Math.PI) - Math.PI;  // normalise to -180..+180ยบ

        Coord co2 = new Coord(lat2 / (Math.PI / 180), lon2 / (Math.PI / 180));
        XY xy2 = toXY(co2);

                String style = "marker-end:url(#Arrow2Send)";
                if (dashed)
                        style += ";stroke-dasharray:2 2";
                route.addElement("line")
                                .addAttribute("x1", xy1.getX())
                                .addAttribute("x2", xy2.getX())
                                .addAttribute("y1", xy1.getY())
                                .addAttribute("y2", xy2.getY())
                                .addAttribute("stroke", colour)
                                .addAttribute("stroke-width", "2")
                                .addAttribute("style", style);
                return xy2;
        }

        private XY toXY(Coord coord) {
                return doc.coord(coord.getLatitude(), coord.getLongitude());
        }

    /* === Main entry point === */
        public void run(SvgDoc doc, String name) {
                this.doc = doc;
                this.route = doc.getRouting();
                this.info = doc.getInfo();
        this.roads = doc.getRoads();
        this.spokes = doc.getSpokes();

                this.setOutStream(System.err);
                this.display(name, "NOD");
        }
}