/*
* Copyright (C) 2010.
*
* 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.main;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.io.Reader;
import java.lang.reflect.Field;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.Objects;
import java.util.Set;
import java.util.regex.Pattern;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import org.xml.sax.SAXException;
import uk.me.parabola.imgfmt.FormatException;
import uk.me.parabola.imgfmt.Utils;
import uk.me.parabola.imgfmt.app.Area;
import uk.me.parabola.imgfmt.app.Coord;
import uk.me.parabola.imgfmt.app.net.GeneralRouteRestriction;
import uk.me.parabola.imgfmt.app.net.RoadDef;
import uk.me.parabola.mkgmap.general.LevelInfo;
import uk.me.parabola.mkgmap.general.MapCollector;
import uk.me.parabola.mkgmap.general.MapElement;
import uk.me.parabola.mkgmap.general.MapLine;
import uk.me.parabola.mkgmap.general.MapPoint;
import uk.me.parabola.mkgmap.general.MapRoad;
import uk.me.parabola.mkgmap.general.MapShape;
import uk.me.parabola.mkgmap.osmstyle.ActionRule;
import uk.me.parabola.mkgmap.osmstyle.ExpressionRule;
import uk.me.parabola.mkgmap.osmstyle.StyleFileLoader;
import uk.me.parabola.mkgmap.osmstyle.StyleImpl;
import uk.me.parabola.mkgmap.osmstyle.StyledConverter;
import uk.me.parabola.mkgmap.osmstyle.TypeReader;
import uk.me.parabola.mkgmap.osmstyle.actions.ActionList;
import uk.me.parabola.mkgmap.osmstyle.actions.ActionReader;
import uk.me.parabola.mkgmap.osmstyle.eval.ExpressionReader;
import uk.me.parabola.mkgmap.osmstyle.eval.Op;
import uk.me.parabola.mkgmap.reader.osm.Element;
import uk.me.parabola.mkgmap.reader.osm.ElementSaver;
import uk.me.parabola.mkgmap.reader.osm.FeatureKind;
import uk.me.parabola.mkgmap.reader.osm.GType;
import uk.me.parabola.mkgmap.reader.osm.Node;
import uk.me.parabola.mkgmap.reader.osm.OsmConverter;
import uk.me.parabola.mkgmap.reader.osm.Relation;
import uk.me.parabola.mkgmap.reader.osm.Rule;
import uk.me.parabola.mkgmap.reader.osm.Style;
import uk.me.parabola.mkgmap.reader.osm.TypeResult;
import uk.me.parabola.mkgmap.reader.osm.WatchableTypeResult;
import uk.me.parabola.mkgmap.reader.osm.Way;
import uk.me.parabola.mkgmap.reader.osm.xml.OsmXmlHandler;
import uk.me.parabola.mkgmap.reader.osm.xml.OsmXmlHandler.SaxHandler;
import uk.me.parabola.mkgmap.scan.SyntaxException;
import uk.me.parabola.mkgmap.scan.Token;
import uk.me.parabola.mkgmap.scan.TokenScanner;
import uk.me.parabola.util.EnhancedProperties;
/**
* Test style rules by converting to a text format, rather than a .img file.
* In addition you can specify a .osm file and a style file separately.
*
* <h2>Single test file</h2>
* The format of the file is as follows
*
* <pre>
* WAY 42
* highway=primary
* oneway=reverse
*
* <<<lines>>>
* highway=primary [0x3 road_class=2 road_speed=2]
* power=line [0x29 resolution 20]
* </pre>
*
* You can have any number of ways, each must end with a blank line.
* A way will be created with two points (1,1),(2,2) (so you can see the
* action of oneway=reverse) and the tags that you specify. If you give
* a number after WAY it will be printed on output so that if you have more
* than one you can tell which is which. If the number is omitted it will
* default to 1.
*
* You can have as many rules as you like after the <<<lines>>> and you
* can include any other style files such as <<<options>>> or <<<info>>> if
* you like.
*
* <h2>osm file mode</h2>
* Takes two arguments, first the style file and then the osm file.
*
* You can give a --reference flag and it will run style file in reference mode,
* that is each rule will be applied to the element without any attempt at
* optimisation. This acts as an independent check of the main style code
* which may have more optimisations.
*
* @author Steve Ratcliffe
*/
public class StyleTester
implements OsmConverter
{
private static final Pattern SPACES_PATTERN =
Pattern.
compile(" +");
private static final Pattern EQUAL_PATTERN =
Pattern.
compile("=");
private static final String STYLETESTER_STYLE =
"styletester.style";
private static PrintStream out =
System.
out;
private static boolean reference
;
private final OsmConverter converter
;
private static boolean forceUseOfGiven
;
private static boolean showMatches
;
private static boolean print =
true;
private StyleTester
(String stylefile, MapCollector coll,
boolean reference
) throws FileNotFoundException {
if (reference
)
converter = makeStrictStyleConverter
(stylefile, coll
);
else
converter = makeStyleConverter
(stylefile, coll
);
}
public static void main
(String...
args) throws IOException {
String[] a = processOptions
(args
);
if (a.
length ==
1)
runSimpleTest
(a
[0]);
else
runTest
(a
[0], a
[1]);
}
public static void setOut
(PrintStream out
) {
StyleTester.
out = out
;
}
private static String[] processOptions
(String...
args) {
List<String> a =
new ArrayList<>();
for (String s : args
) {
if (s.
startsWith("--reference")) {
System.
out.
println("# using reference method of calculation");
reference =
true;
} else if (s.
startsWith("--show-matches")) {
if (!reference
)
System.
out.
println("# using reference method of calculation");
reference =
true;
showMatches =
true;
} else if (s.
startsWith("--no-print")) {
print =
false;
} else {
a.
add(s
);
}
}
return a.
toArray(new String[a.
size()]);
}
private static void runTest
(String stylefile,
String mapfile
) {
PrintingMapCollector collector =
new PrintingMapCollector
();
OsmConverter normal
;
try {
normal =
new StyleTester
(stylefile, collector, reference
);
} catch (FileNotFoundException e
) {
System.
err.
println("Could not open style file " + stylefile
);
return;
}
try {
InputStream is = Utils.
openFile(mapfile
);
SAXParserFactory parserFactory =
SAXParserFactory.
newInstance();
parserFactory.
setXIncludeAware(true);
parserFactory.
setNamespaceAware(true);
SAXParser parser = parserFactory.
newSAXParser();
try {
EnhancedProperties props =
new EnhancedProperties
();
props.
put("preserve-element-order",
"1");
ElementSaver saver =
new ElementSaver
(props
);
OsmXmlHandler handler =
new OsmXmlHandler
();
SaxHandler saxHandler = handler.
new SaxHandler
();
handler.
setElementSaver(saver
);
parser.
parse(is, saxHandler
);
saver.
finishLoading();
saver.
convert(normal
);
System.
err.
println("Conversion time " +
(System.
currentTimeMillis() - collector.
getStart()) +
"ms");
} catch (IOException e
) {
throw new FormatException
("Error reading file", e
);
}
} catch (SAXException e
) {
throw new FormatException
("Error parsing file", e
);
} catch (ParserConfigurationException e
) {
throw new FormatException
("Internal error configuring xml parser", e
);
} catch (FileNotFoundException e
) {
System.
err.
println("Cannot open file " + mapfile
);
}
}
/**
* Run a simple test with a combined test file.
* @param filename The test file contains text way definitions and a style
* file all in one.
*/
public static void runSimpleTest
(String filename
) {
// 14Jan20 Changed from using DefaultCharset to UTF-8
try (BufferedReader br =
new BufferedReader(new InputStreamReader(new FileInputStream(filename
), StandardCharsets.
UTF_8))) {
List<Way
> ways = readSimpleTestFile
(br
);
List<String> givenList = readGivenResults
();
boolean noStrict =
false;
if (!givenList.
isEmpty() && Objects.
equals(givenList.
get(0),
"NO-STRICT")) {
givenList.
remove(0);
noStrict =
true;
}
List<MapElement
> strictResults =
new ArrayList<>();
List<MapElement
> results =
new ArrayList<>();
List<String> actual =
new ArrayList<>();
List<String> expected =
new ArrayList<>();
for (Way w : ways
) {
OsmConverter normal =
new StyleTester
(STYLETESTER_STYLE,
new LocalMapCollector
(results
),
false);
String prefix =
"WAY " + w.
getId() +
": ";
normal.
convertWay(w.
copy());
normal.
end();
actual.
addAll(formatResults
(prefix, results
));
results.
clear();
if (!noStrict
) {
OsmConverter strict =
new StyleTester
(STYLETESTER_STYLE,
new LocalMapCollector
(strictResults
),
true);
strict.
convertWay(w.
copy());
strict.
end();
expected.
addAll(formatResults
(prefix, strictResults
));
strictResults.
clear();
}
}
printResult
("", actual
);
if (!noStrict
&& !Objects.
equals(actual, expected
)) {
out.
println("ERROR expected result is:");
printResult
("|", expected
);
}
if ((!givenList.
isEmpty() || forceUseOfGiven
) && !Objects.
equals(actual, givenList
)) {
out.
println("ERROR given results were:");
printResult
("", givenList
);
}
} catch (FileNotFoundException e
) {
System.
err.
println("Cannot open test file " + filename
);
} catch (IOException e
) {
System.
err.
println("Failure while reading test file " + filename
);
}
}
private static List<String> readGivenResults
() {
List<String> givenResults =
new ArrayList<>();
//BufferedReader br = null;
try (StyleFileLoader fileLoader = StyleFileLoader.
createStyleLoader(STYLETESTER_STYLE,
null);
Reader reader = fileLoader.
open("results");
BufferedReader br =
new BufferedReader(reader
)
) {
String line
;
while ((line = br.
readLine()) !=
null) {
line = line.
trim();
if (line.
isEmpty())
continue;
givenResults.
add(line
);
}
} catch (IOException e
) {
// there are no known good results given, that is OK
}
return givenResults
;
}
@
Override
public void convertWay
(Way way
) {
converter.
convertWay(way
);
}
@
Override
public void convertNode
(Node node
) {
converter.
convertNode(node
);
}
@
Override
public void convertRelation
(Relation relation
) {
converter.
convertRelation(relation
);
}
@
Override
public void setBoundingBox
(Area bbox
) {
converter.
setBoundingBox(bbox
);
}
@
Override
public void end
() {
converter.
end();
}
private static void printResult
(String prefix,
List<String> results
) {
for (String s : results
) {
out.
println(prefix + s
);
}
}
/**
* Read in the combined test file. This contains some ways and a style.
* The style does not need to include 'version' as this is added for you.
*/
private static List<Way
> readSimpleTestFile
(BufferedReader br
) throws IOException {
List<Way
> ways =
new ArrayList<>();
String line
;
while ((line = br.
readLine()) !=
null) {
line = line.
trim();
if (line.
toLowerCase(Locale.
ENGLISH).
startsWith("way")) {
Way w = readWayTags
(br, line
);
ways.
add(w
);
} else if (line.
startsWith("<<<")) {
// read the rest of the file
readStyles
(br, line
);
}
}
return ways
;
}
/**
* You can have a number of ways defined in the file. If you give a
* number after 'way' that is used as the way id so that you can identify
* it in the results.
*
* A list of tags are read and added to the way up until a blank line.
*
* @param br Read from here.
* @param waydef This will contain the way-id if one was given. Otherwise
* the way id will be 1.
* @throws IOException If the file cannot be read.
*/
private static Way readWayTags
(BufferedReader br,
String waydef
) throws IOException {
int id =
1;
String[] strings = SPACES_PATTERN.
split(waydef
);
if (strings.
length > 1)
id =
Integer.
parseInt(strings
[1]);
Way w =
new Way
(id
);
w.
addPoint(new Coord
(1,
1));
w.
addPoint(new Coord
(2,
2));
String line
;
while ((line = br.
readLine()) !=
null) {
if (line.
indexOf('=') < 0)
break;
String[] tagval = EQUAL_PATTERN.
split(line,
2);
if (tagval.
length ==
2)
w.
addTag(tagval
[0], tagval
[1]);
}
return w
;
}
/**
* Print out the garmin elements that were produced by the rules.
* @param prefix This string will be prepended to the formatted result.
* @param lines The resulting map elements.
*/
private static List<String> formatResults
(String prefix,
List<MapElement
> lines
) {
List<String> result =
new ArrayList<>();
for (MapElement el : lines
) {
String s
;
// So we can run against versions that do not have toString() methods
if (el
instanceof MapRoad
)
s = roadToString
((MapRoad
) el
);
else
s = lineToString
((MapLine
) el
);
result.
add(prefix + s
);
}
return result
;
}
/**
* This is so we can run against versions of mkgmap that do not have
* toString methods on MapLine and MapRoad.
*/
private static String lineToString
(MapLine el
) {
StringBuilder sb =
new StringBuilder();
sb.
append(String.
format("Line 0x%x, labels=%s, res=%d-%d",
el.
getType(),
Arrays.
toString(el.
getLabels()),
el.
getMinResolution(), el.
getMaxResolution()));
if (el.
isDirection())
sb.
append(" oneway");
sb.
append(' ');
for (Coord co : el.
getPoints())
sb.
append(String.
format("(%s),", co.
toGarminString()));
return sb.
toString();
}
/**
* This is so we can run against versions of mkgmap that do not have
* toString methods on MapLine and MapRoad.
*/
private static String roadToString
(MapRoad el
) {
StringBuilder sb =
new StringBuilder(lineToString
(el
));
sb.
replace(0,
4,
"Road");
sb.
append(String.
format(" road class=%d speed=%d", el.
getRoadDef().
getRoadClass(),
getRoadSpeed
(el.
getRoadDef())));
return sb.
toString();
}
/**
* Implement a method to get the road speed from RoadDef.
*/
private static int getRoadSpeed
(RoadDef roadDef
) {
try {
Field field = RoadDef.
class.
getDeclaredField("tabAInfo");
field.
setAccessible(true);
int tabA =
(Integer) field.
get(roadDef
);
return tabA
& 0x7
;
} catch (NoSuchFieldException |
IllegalAccessException e
) {
e.
printStackTrace();
}
return 0;
}
/**
* Read the style definitions. The rest of the file is just copied to
* a style file named 'styletester.style' so that it can be read in the
* normal manner.
* @param br Read from here.
* @param initLine The first line of the style definition that has already been read.
* @throws IOException If writing fails.
*/
private static void readStyles
(BufferedReader br,
String initLine
) throws IOException {
FileWriter writer =
new FileWriter(STYLETESTER_STYLE
);
PrintWriter pw =
new PrintWriter(writer
);
pw.
println("<<<version>>>\n0");
pw.
println(initLine
);
try {
String line
;
while ((line = br.
readLine()) !=
null)
pw.
println(line
);
} finally {
pw.
close();
}
}
/**
* A styled converter that should work exactly the same as the version of
* mkgmap you are using.
* @param styleFile The name of the style file to process.
* @param coll A map collector to receive the created elements.
*/
private static StyledConverter makeStyleConverter
(String styleFile, MapCollector coll
) throws FileNotFoundException {
Style style =
new StyleImpl
(styleFile,
null);
return new StyledConverter
(style, coll,
new EnhancedProperties
());
}
/**
* A special styled converted that attempts to produce the correct theoretical
* result of running the style rules in order by literally doing that.
* This should produce the same result as {@link #makeStyleConverter} and
* can be used as a test of the strict style ordering branch.
* @param styleFile The name of the style file to process.
* @param coll A map collector to receive the created elements.
*/
private StyledConverter makeStrictStyleConverter
(String styleFile, MapCollector coll
) throws FileNotFoundException {
Style style =
new ReferenceStyle
(styleFile,
null);
return new StyledConverter
(style, coll,
new EnhancedProperties
());
}
public static void forceUseOfGiven
() {
forceUseOfGiven =
true;
}
/**
* This is a reference implementation of the style engine which is somewhat
* independent of the main implementation and does not have any kind of
* optimisations. You can compare the results from the two implementations
* to find bugs and regressions.
*/
private class ReferenceStyle
extends StyleImpl
{
private final StyleFileLoader fileLoader
;
private LevelInfo
[] levels
;
/**
* Create a style from the given location and name.
*
* @param loc The location of the style. Can be null to mean just check the
* classpath.
* @param name The name. Can be null if the location isn't. If it is null
* then we just check for the first version file that can be found.
* @throws FileNotFoundException If the file doesn't exist. This can include
* the version file being missing.
*/
ReferenceStyle
(String loc,
String name
) throws FileNotFoundException {
super(loc, name
);
fileLoader = StyleFileLoader.
createStyleLoader(loc, name
);
setupReader
();
}
private void setupReader
() {
String l = LevelInfo.
DEFAULT_LEVELS;
levels = LevelInfo.
createFromString(l
);
}
/**
* Throws away the rules as previously read and reads again using the
* SimpleRuleFileReader which does not re-order or optimise the rules
* in any way.
*
* @return A simple list of rules with a resolving method that applies
* each rule in turn to the element until there is match.
*/
@
Override
public Rule getWayRules
() {
ReferenceRuleSet r =
new ReferenceRuleSet
();
r.
addAll((ReferenceRuleSet
) getLineRules
());
r.
addAll((ReferenceRuleSet
) getPolygonRules
());
return r
;
}
/**
* Throws away the existing rules for the lines and re-reads them using
* the SimpleRuleFileReader that does not re-order or optimise the rules in any
* way.
*
* @return A Reference rule set of the lines.
*/
@
Override
public Rule getLineRules
() {
ReferenceRuleSet r =
new ReferenceRuleSet
();
SimpleRuleFileReader ruleFileReader =
new SimpleRuleFileReader
(FeatureKind.
POLYLINE, levels, r
);
try {
ruleFileReader.
load(fileLoader,
"lines");
} catch (FileNotFoundException e
) {
e.
printStackTrace();
}
return r
;
}
/**
* Throws away the existing rules for the polygons and re-reads them using
* the SimpleRuleFileReader that does not re-order or optimise the rules in any
* way.
*
* @return A Reference rule set of the polygons.
*/
@
Override
public Rule getPolygonRules
() {
ReferenceRuleSet r =
new ReferenceRuleSet
();
SimpleRuleFileReader ruleFileReader =
new SimpleRuleFileReader
(FeatureKind.
POLYGON, levels, r
);
try {
ruleFileReader.
load(fileLoader,
"polygons");
} catch (FileNotFoundException e
) {
// not a problem
}
return r
;
}
@
Override
public Rule getRelationRules
() {
ReferenceRuleSet r =
new ReferenceRuleSet
();
SimpleRuleFileReader ruleFileReader =
new SimpleRuleFileReader
(FeatureKind.
RELATION, levels, r
);
try {
ruleFileReader.
load(fileLoader,
"relations");
} catch (FileNotFoundException e
) {
// its not a problem
}
return r
;
}
@
Override
public Set<String> getUsedTags
() {
return null;
}
/**
* Keeps each rule in an ordered list.
*
* Types are resolved by literally applying the rules in order to the
* element.
*
* As long as the rules are added in the order they are encountered in
* the file, this should work.
*/
private class ReferenceRuleSet
implements Rule
{
private final List<Rule
> rules =
new ArrayList<>();
int cacheId
;
public void add
(Rule rule
) {
rules.
add(rule
);
}
public void addAll
(ReferenceRuleSet rs
) {
for (Rule r : rs.
rules) {
add
(r
);
}
}
public void resolveType
(Element el, TypeResult result
) {
String tagsBefore = el.
toTagString();
if (showMatches
) {
out.
println("# Tags before: " + tagsBefore
);
}
WatchableTypeResult a =
new WatchableTypeResult
(result
);
// Start by literally running through the rules in order.
for (Rule rule : rules
) {
a.
reset();
cacheId = rule.
resolveType(cacheId, el, a
);
if (showMatches
) {
if (a.
isFound()) {
out.
println("# Matched: " + rule
);
} else if (a.
isActionsOnly()) {
out.
println("# Matched for actions: " + rule
);
}
}
if (a.
isResolved())
break;
}
if (showMatches
&& !Objects.
equals(tagsBefore, el.
toTagString()))
out.
println("# Way tags after: " + el.
toTagString());
}
@
Override
public int resolveType
(int cacheId,
Element el, TypeResult result
) {
resolveType
(el, result
);
return cacheId
;
}
public void setFinalizeRule
(Rule finalizeRule
) {
for (Rule rule : rules
) {
rule.
setFinalizeRule(finalizeRule
);
}
}
@
Override
public Rule getFinalizeRule
() {
if (rules.
isEmpty())
return null;
return rules.
get(0).
getFinalizeRule();
}
@
Override
public void printStats
(String header
) {
// TODO Auto-generated method stub
}
@
Override
public boolean containsExpression
(String exp
) {
if (rules.
isEmpty()) {
// this method must be called after prepare() is called so
// that we have rules to which the finalize rules can be applied
throw new IllegalStateException("First call prepare() before setting the finalize rules");
}
for (Rule rule : rules
){
if (rule.
containsExpression(exp
))
return true;
}
return getFinalizeRule
()!=
null && getFinalizeRule
().
containsExpression(exp
);
}
}
/**
* A reimplementation of RuleFileReader that does no optimisation but
* just reads the rules into a list.
*
* Again this can be compared with the main implementation which may
* attempt more optimisations.
*/
class SimpleRuleFileReader
{
private final TypeReader typeReader
;
private final ReferenceRuleSet rules
;
private ReferenceRuleSet finalizeRules
;
private TokenScanner scanner
;
private boolean inFinalizeSection
;
SimpleRuleFileReader
(FeatureKind kind, LevelInfo
[] levels, ReferenceRuleSet rules
) {
this.
rules = rules
;
typeReader =
new TypeReader
(kind, levels
);
}
/**
* Read a rules file.
* @param loader A file loader.
* @param name The name of the file to open.
* @throws FileNotFoundException If the given file does not exist.
*/
public void load
(StyleFileLoader loader,
String name
) throws FileNotFoundException {
Reader r = loader.
open(name
);
load
(r, name
);
}
void load
(Reader r,
String name
) {
scanner =
new TokenScanner
(name, r
);
scanner.
setExtraWordChars("-:");
ExpressionReader expressionReader =
new ExpressionReader
(scanner, FeatureKind.
POLYLINE);
ActionReader actionReader =
new ActionReader
(scanner
);
// Read all the rules in the file.
scanner.
skipSpace();
while (!scanner.
isEndOfFile()) {
if (checkCommand
(scanner
))
continue;
Op expr = expressionReader.
readConditions();
ActionList actions = actionReader.
readActions();
// If there is an action list, then we don't need a type
GType type =
null;
if (scanner.
checkToken("["))
type = typeReader.
readType(scanner
);
else if (actions ==
null)
throw new SyntaxException
(scanner,
"No type definition given");
saveRule
(expr, actions, type
);
scanner.
skipSpace();
}
if (finalizeRules
!=
null) {
rules.
setFinalizeRule(finalizeRules
);
}
}
private boolean checkCommand
(TokenScanner scanner
) {
scanner.
skipSpace();
if (scanner.
isEndOfFile())
return false;
if (!inFinalizeSection
&& scanner.
checkToken("<")) {
Token token = scanner.
nextToken();
if (scanner.
checkToken("finalize")) {
Token finalizeToken = scanner.
nextToken();
if (scanner.
checkToken(">")) {
// consume the > token
scanner.
nextToken();
// mark start of the finalize block
inFinalizeSection =
true;
finalizeRules =
new ReferenceRuleSet
();
return true;
} else {
scanner.
pushToken(finalizeToken
);
scanner.
pushToken(token
);
}
} else {
scanner.
pushToken(token
);
}
}
scanner.
skipSpace();
return false;
}
/**
* Save the expression as a rule.
*/
private void saveRule
(Op op, ActionList actions, GType gt
) {
Rule rule
;
if (actions.
isEmpty())
rule =
new ExpressionRule
(op, gt
);
else
rule =
new ActionRule
(op, actions.
getList(), gt
);
if (inFinalizeSection
)
finalizeRules.
add(rule
);
else
rules.
add(rule
);
}
}
}
/**
* A map collector that just adds any line or road we find to the end of
* a list.
*/
private static class LocalMapCollector
implements MapCollector
{
private final List<MapElement
> lines
;
private LocalMapCollector
(List<MapElement
> lines
) {
this.
lines = lines
;
}
public void addToBounds
(Coord p
) { }
// could save points in the same way as lines to test them
public void addPoint
(MapPoint point
) { }
public void addLine
(MapLine line
) {
lines.
add(line
);
}
public void addShape
(MapShape shape
) { }
public void addRoad
(MapRoad road
) {
lines.
add(road
);
}
public int addRestriction
(GeneralRouteRestriction grr
) {
return 0;
}
}
/**
* A map collector that just prints elements found.
* (lines and roads only at present).
*/
private static class PrintingMapCollector
implements MapCollector
{
private long start
;
public void addToBounds
(Coord p
) { if (start ==
0) {
System.
err.
println("start collection");
start =
System.
currentTimeMillis();
}}
// could save points in the same way as lines to test them
public void addPoint
(MapPoint point
) { }
public void addLine
(MapLine line
) {
if (start ==
0) {
System.
err.
println("start collection");
start =
System.
currentTimeMillis();
}
if (print
) {
List<String> strings = formatResults
("",
Collections.
singletonList(line
));
printResult
("", strings
);
}
}
public void addShape
(MapShape shape
) { }
public void addRoad
(MapRoad road
) {
if (print
) {
List<String> strings = formatResults
("",
Collections.
singletonList(road
));
printResult
("", strings
);
}
}
public int addRestriction
(GeneralRouteRestriction grr
) {
return 0;
}
public long getStart
() {
return start
;
}
}
}