Rev 373 |
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.Formatter;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import uk.me.parabola.imgfmt.Utils;
import uk.me.parabola.imgfmt.app.Coord;
import uk.me.parabola.imgfmt.app.net.NODHeader;
import org.dom4j.Element;
import test.display.CommonDisplay;
import test.elements.Line;
import test.files.Nod2Record;
import test.files.RoadData;
import test.files.RouteArc;
import test.files.RouteCenter;
import test.files.RouteNode;
import test.files.Segment;
/**
* Convert a nod file to svg to show the routing relationships.
*
* @author Steve Ratcliffe
*/
public class Route
extends CommonDisplay
{
private SvgDoc doc
;
private Set<Integer> nodes
;
private Element route
;
private Element info
;
private Element roads
;
private Element spokes
;
private Element marked
;
private final Set<Integer> roadIds =
new HashSet<>();
private Set<Integer> centers =
new HashSet<>();
protected void print
() {
openLbl
();
openTre
();
openNet
();
openRgn
();
reader.
position(0);
openNod
();
List<RouteCenter
> routeCenters = nod.
readNodeCenters();
initRoads
();
int pos =
0;
int end =
((NODHeader
) nod.
getHeader()).
getRoadSection().
getSize();
do {
Nod2Record nod2 = nod.
getNod2(pos
);
RouteNode node = nod2.
getNode();
if (nodes.
contains(node.
getOffset()))
{
int offset = nod2.
getRoadData().
getOffset();
System.
out.
printf("adding road %x\n", offset
);
roadIds.
add(offset
);
}
pos =
(int) nod2.
getNext();
} while (pos
< end
);
// If we have nodes, then get the set of centers that will cover them.
if (!nodes.
isEmpty()) {
for (int n : nodes
) {
RouteNode node = nod.
getNode(n
);
centers.
add(node.
getCenter().
getIndex());
if (node.
getRoad() !=
null) {
System.
out.
printf("n:%x adding road %x\n", node.
getOffset(), node.
getRoad().
getOffset());
roadIds.
add(node.
getRoad().
getOffset());
}
}
}
// If we got no centers, then use the first 3 (or however many there are)
if (centers.
isEmpty()) {
for (int i =
0; i
< Math.
min(4, routeCenters.
size()); i++
)
centers.
add(i
);
}
// Now draw each of the wanted centers.
for (int i : centers
) {
RouteCenter c = routeCenters.
get(i
);
displayCenter
(c
);
}
drawRoads
();
}
private void displayCenter
(RouteCenter c
) {
XY xy1 = toXY
(c.
getCoord());
route.
addElement("circle")
.
addAttribute("cx", xy1.
getX())
.
addAttribute("cy", xy1.
getY())
.
addAttribute("r",
"10")
.
addAttribute("fill",
"none")
.
addAttribute("stroke",
"orange")
.
addAttribute("stroke-width",
"3");
route.
addElement("text")
.
addAttribute("x", xy1.
getX(-
7))
.
addAttribute("y", xy1.
getY(+
5))
.
setText(String.
valueOf(c.
getIndex()));
for (RouteNode node : c.
nodes().
values()) {
if (node.
isBad())
continue;
drawRouteNode
(node
);
if (node.
hasArcs()) {
for (RouteArc arc : node.
getArcs()) {
if (arc.
isBad())
continue;
drawArc
(arc
);
}
}
}
}
private void drawRouteNode
(RouteNode node
) {
boolean markedNode = nodes.
contains(node.
getOffset());
XY xy = toXY
(node.
getCoord());
Element circ =
(markedNode
? marked: route
).
addElement("circle")
.
addAttribute("cx", xy.
getX())
.
addAttribute("cy", xy.
getY())
.
addAttribute("r",
"3")
;
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()));
Element rect = g.
addElement("rect")
.
addAttribute("width",
"50")
.
addAttribute("height",
"10")
.
addAttribute("fill",
"white")
.
addAttribute("opacity",
"0.7")
.
addAttribute("stroke-width",
"0.5")
.
addAttribute("stroke",
"black");
if (markedNode
)
rect.
addAttribute("fill",
"#ff0081");
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;
}
Element layer =
(nodes.
contains(arc.
getNode().
getOffset()))? marked: route
;
RouteNode n1 = arc.
getNode();
RouteNode n2 = nod.
getNode(arc.
getLink());
if (n2
!=
null) {
assert arc.
hasDirection();
// Draw an arrow third way to the target node.
XY p1 = toXY
(n1.
getCoord());
XY p2 = toXY
(n2.
getCoord());
int x2 = p1.
getXi() +
(p2.
getXi() - p1.
getXi())/
3;
int y2 = p1.
getYi() +
(p2.
getYi() - p1.
getYi())/
3;
String style =
"marker-end:url(#Arrow2Send)";
if (arc.
hasCurve())
style +=
";stroke-dasharray:2 2";
layer.
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", style
)
;
// Add info on the arc.
Element g = layer.
addElement("g")
.
addAttribute("transform",
String.
format("translate(%d,%d)", x2, y2
));
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(), nod.
rawToMeters(arc.
getDistance()), arc.
getInitialDegrees(),
arc.
getDirection()));
// Add the rest of the arrow.
layer.
addElement("line")
.
addAttribute("x1",
String.
valueOf(x2
))
.
addAttribute("y1",
String.
valueOf(y2
))
.
addAttribute("x2", p2.
getX())
.
addAttribute("y2", p2.
getY())
.
addAttribute("stroke", colour
)
.
addAttribute("stroke-wdith",
"1")
.
addAttribute("style", style
);
markDirection
(layer, n1.
getCoord(), arc.
getInitialDegrees(),
50, colour,
false);
}
}
private void drawRoads
() {
for (Integer i : roadIds
) {
RoadData road = net.
getRoad(i
);
int part =
0;
List<Segment> segments = road.
getSegments();
int nseg = segments.
size();
for (Segment seg : segments
) {
Line line = seg.
getLine();
Coord first = line.
getFirstPoint();
XY xy1 = toXY
(first
);
Coord next = line.
getCoords().
get(1);
XY p = xy1.
offset(toXY
(next
),
2);
roads.
addElement("text")
.
addAttribute("x", p.
getX())
.
addAttribute("y", p.
getY())
.
addAttribute("font-size",
"3px")
.
setText(String.
format("Road:%x %d of %d", road.
getOffset(),
part+
1, nseg
));
drawLine
(line, part == nseg-
1);
part++
;
}
}
}
private void drawLine
(Line line,
boolean last
) {
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;
case 4: colour =
"yellow"; break;
default: colour =
"#775599"; break;
}
roads.
addElement("path")
.
addAttribute("stroke", colour
)
.
addAttribute("fill",
"none")
.
addAttribute("stroke-width",
"2")
.
addAttribute("style",
last
? "marker-end:url(#Arrow2Send)" :
"marker-end:url(#DiamondSend)")
.
addAttribute("d", fmt.
toString());
for (Coord co : line.
getCoords()) {
if (co.
getId() > 0) {
XY point = toXY
(co
);
roads.
addElement("circle")
.
addAttribute("cx", point.
getX())
.
addAttribute("cy", point.
getY())
.
addAttribute("fill", colour
)
.
addAttribute("r",
"2");
}
}
}
/**
* 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 layer
* @param co Draw from here
* @param dir Direction (bearing) of arrow in degrees.
* @param len Length in meters.
* @param colour Colour to draw it in.
* @param dashed
*/
private Coord markDirection
(Element layer, 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(#DotM)";
if (dashed
)
style +=
";stroke-dasharray:2 2";
layer.
addElement("line")
.
addAttribute("x1", xy1.
getX())
.
addAttribute("x2", xy2.
getX())
.
addAttribute("y1", xy1.
getY())
.
addAttribute("y2", xy2.
getY())
.
addAttribute("stroke", colour
)
.
addAttribute("stroke-width",
"0.5")
.
addAttribute("style", style
);
return co2
;
}
private XY toXY
(Coord coord
) {
return doc.
coord(coord.
getLatitude(), coord.
getLongitude());
}
/* === Main entry point === */
public void run
(SvgDoc doc,
String name,
Set<Integer> nodes,
Set<Integer> centers
) {
this.
doc = doc
;
this.
nodes = nodes
;
this.
centers = centers
;
this.
route = doc.
getRouting();
this.
info = doc.
getInfo();
this.
roads = doc.
getRoads();
this.
spokes = doc.
getSpokes();
this.
marked = doc.
getMarked();
this.
setOutStream(System.
err);
this.
display(name,
"NOD");
}
}