unify RenderTheme 'node'/'way','closed' rules to match any 'element'
This commit is contained in:
parent
4dfb3eed91
commit
38b3443927
@ -18,10 +18,9 @@ import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.oscim.core.GeometryBuffer.GeometryType;
|
||||
import org.oscim.core.MapElement;
|
||||
import org.oscim.theme.renderinstruction.RenderInstruction;
|
||||
import org.oscim.theme.rule.Closed;
|
||||
import org.oscim.theme.rule.Element;
|
||||
import org.oscim.theme.rule.Rule;
|
||||
import org.oscim.utils.LRUCache;
|
||||
import org.xml.sax.Attributes;
|
||||
@ -91,6 +90,7 @@ public class RenderTheme implements IRenderTheme {
|
||||
private Rule[] mRules;
|
||||
|
||||
class ElementCache {
|
||||
final int matchType;
|
||||
final LRUCache<MatchingCacheKey, RenderInstructionItem> cache;
|
||||
final MatchingCacheKey cacheKey;
|
||||
|
||||
@ -99,10 +99,11 @@ public class RenderTheme implements IRenderTheme {
|
||||
|
||||
RenderInstructionItem prevItem;
|
||||
|
||||
public ElementCache() {
|
||||
public ElementCache(int type) {
|
||||
cache = new LRUCache<MatchingCacheKey, RenderInstructionItem>(MATCHING_CACHE_SIZE);
|
||||
instructionList = new ArrayList<RenderInstruction>(4);
|
||||
cacheKey = new MatchingCacheKey();
|
||||
matchType = type;
|
||||
}
|
||||
}
|
||||
|
||||
@ -121,9 +122,9 @@ public class RenderTheme implements IRenderTheme {
|
||||
mBaseTextSize = baseTextSize;
|
||||
|
||||
mElementCache = new ElementCache[3];
|
||||
for (int i = 0; i < 3; i++)
|
||||
mElementCache[i] = new ElementCache();
|
||||
|
||||
mElementCache[0] = new ElementCache(Element.NODE);
|
||||
mElementCache[1] = new ElementCache(Element.LINE);
|
||||
mElementCache[2] = new ElementCache(Element.POLY);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -214,16 +215,8 @@ public class RenderTheme implements IRenderTheme {
|
||||
List<RenderInstruction> matches = cache.instructionList;
|
||||
matches.clear();
|
||||
|
||||
if (element.type == GeometryType.LINE) {
|
||||
for (Rule rule : mRules)
|
||||
rule.matchWay(element.tags, zoomMask, Closed.NO, matches);
|
||||
} else if (element.type == GeometryType.POLY) {
|
||||
for (Rule rule : mRules)
|
||||
rule.matchWay(element.tags, zoomMask, Closed.YES, matches);
|
||||
} else {
|
||||
for (Rule rule : mRules)
|
||||
rule.matchNode(element.tags, zoomMask, matches);
|
||||
}
|
||||
for (Rule rule : mRules)
|
||||
rule.matchElement(cache.matchType, element.tags, zoomMask, matches);
|
||||
|
||||
int size = matches.size();
|
||||
if (size > 1) {
|
||||
|
||||
@ -117,12 +117,13 @@ public class RenderThemeHandler extends DefaultHandler {
|
||||
private Rule mCurrentRule;
|
||||
|
||||
private final Stack<Element> mElementStack = new Stack<Element>();
|
||||
private int mLevel;
|
||||
private RenderTheme mRenderTheme;
|
||||
private final Stack<Rule> mRuleStack = new Stack<Rule>();
|
||||
private final HashMap<String, RenderInstruction> tmpStyleHash =
|
||||
new HashMap<String, RenderInstruction>(10);
|
||||
|
||||
private int mLevel;
|
||||
private RenderTheme mRenderTheme;
|
||||
|
||||
@Override
|
||||
public void endDocument() {
|
||||
if (mRenderTheme == null) {
|
||||
@ -130,8 +131,11 @@ public class RenderThemeHandler extends DefaultHandler {
|
||||
}
|
||||
|
||||
mRenderTheme.complete(mRulesList, mLevel);
|
||||
|
||||
mRulesList.clear();
|
||||
tmpStyleHash.clear();
|
||||
mRuleStack.clear();
|
||||
mElementStack.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -153,7 +157,6 @@ public class RenderThemeHandler extends DefaultHandler {
|
||||
Log.d(TAG, exception.getMessage());
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void startElement(String uri, String localName, String qName,
|
||||
Attributes attributes) throws SAXException {
|
||||
|
||||
@ -15,8 +15,10 @@
|
||||
*/
|
||||
package org.oscim.theme.rule;
|
||||
|
||||
final class Element {
|
||||
public final class Element {
|
||||
public static final int NODE = 1 << 0;
|
||||
public static final int WAY = 1 << 1;
|
||||
public static final int LINE = 1 << 1;
|
||||
public static final int POLY = 1 << 2;
|
||||
public static final int WAY = LINE | POLY;
|
||||
public static final int ANY = NODE | WAY;
|
||||
}
|
||||
|
||||
@ -21,6 +21,14 @@ import org.oscim.core.Tag;
|
||||
class NegativeMatcher implements AttributeMatcher {
|
||||
private final String[] mKeyList;
|
||||
private final String[] mValueList;
|
||||
|
||||
// - exclusive negation matches when either KEY is not present
|
||||
// or KEY is present and any VALUE is NOT present
|
||||
//
|
||||
// - non-exclusive negation matches when either KEY is not present
|
||||
// or KEY is present and any VALUE is present
|
||||
//
|
||||
// - TODO 'MUST NOT contain key'
|
||||
private final boolean mExclusive;
|
||||
|
||||
NegativeMatcher(List<String> keyList, List<String> valueList, boolean exclusive) {
|
||||
@ -42,25 +50,23 @@ class NegativeMatcher implements AttributeMatcher {
|
||||
|
||||
@Override
|
||||
public boolean matches(Tag[] tags) {
|
||||
if (keyListDoesNotContainKeys(tags)) {
|
||||
if (keyListDoesNotContainKeys(tags))
|
||||
return true;
|
||||
}
|
||||
|
||||
for (Tag tag : tags) {
|
||||
for (Tag tag : tags)
|
||||
for (String value : mValueList)
|
||||
if (value == tag.value)
|
||||
return !mExclusive;
|
||||
}
|
||||
|
||||
return mExclusive;
|
||||
}
|
||||
|
||||
private boolean keyListDoesNotContainKeys(Tag[] tags) {
|
||||
for (Tag tag : tags) {
|
||||
for (Tag tag : tags)
|
||||
for (String key : mKeyList)
|
||||
if (key == tag.key)
|
||||
return false;
|
||||
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Copyright 2010, 2011, 2012 mapsforge.org
|
||||
* Copyright 2013 Hannes Janetzek
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under the
|
||||
* terms of the GNU Lesser General Public License as published by the Free Software
|
||||
@ -19,20 +20,14 @@ import org.oscim.core.Tag;
|
||||
class NegativeRule extends Rule {
|
||||
final AttributeMatcher mAttributeMatcher;
|
||||
|
||||
NegativeRule(int element, int closed, int zoom,
|
||||
AttributeMatcher attributeMatcher) {
|
||||
super(element, closed, zoom);
|
||||
NegativeRule(int element, int zoom, AttributeMatcher attributeMatcher) {
|
||||
super(element, zoom);
|
||||
|
||||
mAttributeMatcher = attributeMatcher;
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean matchesNode(Tag[] tags) {
|
||||
return mAttributeMatcher.matches(tags);
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean matchesWay(Tag[] tags) {
|
||||
boolean matchesTags(Tag[] tags) {
|
||||
return mAttributeMatcher.matches(tags);
|
||||
}
|
||||
}
|
||||
|
||||
@ -20,9 +20,8 @@ class PositiveRule extends Rule {
|
||||
final AttributeMatcher mKeyMatcher;
|
||||
final AttributeMatcher mValueMatcher;
|
||||
|
||||
PositiveRule(int element, int closed, int zoom,
|
||||
AttributeMatcher keyMatcher, AttributeMatcher valueMatcher) {
|
||||
super(element, closed, zoom);
|
||||
PositiveRule(int element, int zoom, AttributeMatcher keyMatcher, AttributeMatcher valueMatcher) {
|
||||
super(element, zoom);
|
||||
|
||||
if (keyMatcher instanceof AnyMatcher)
|
||||
mKeyMatcher = null;
|
||||
@ -36,14 +35,8 @@ class PositiveRule extends Rule {
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean matchesNode(Tag[] tags) {
|
||||
boolean matchesTags(Tag[] tags) {
|
||||
return (mKeyMatcher == null || mKeyMatcher.matches(tags))
|
||||
&& (mValueMatcher == null || mValueMatcher.matches(tags));
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean matchesWay(Tag[] tags) {
|
||||
return (mKeyMatcher == null || mKeyMatcher.matches(tags)) &&
|
||||
(mValueMatcher == null || mValueMatcher.matches(tags));
|
||||
}
|
||||
}
|
||||
|
||||
@ -37,40 +37,57 @@ public abstract class Rule {
|
||||
private static final String STRING_WILDCARD = "*";
|
||||
|
||||
private static Rule createRule(Stack<Rule> ruleStack, int element, String keys,
|
||||
String values, int closed,
|
||||
byte zoomMin, byte zoomMax) {
|
||||
|
||||
List<String> keyList = new ArrayList<String>(Arrays.asList(SPLIT_PATTERN
|
||||
.split(keys)));
|
||||
List<String> valueList = new ArrayList<String>(Arrays.asList(SPLIT_PATTERN
|
||||
.split(values)));
|
||||
String values, byte zoomMin, byte zoomMax) {
|
||||
|
||||
int zoom = 0;
|
||||
for (int z = zoomMin; z <= zoomMax && z < 32; z++)
|
||||
zoom |= (1 << z);
|
||||
|
||||
if (valueList.remove(STRING_NEGATION)) {
|
||||
AttributeMatcher attributeMatcher = new NegativeMatcher(keyList, valueList,
|
||||
false);
|
||||
return new NegativeRule(element, closed, zoom,
|
||||
attributeMatcher);
|
||||
List<String> keyList = null, valueList = null;
|
||||
boolean negativeRule = false;
|
||||
boolean exclusionRule = false;
|
||||
|
||||
AttributeMatcher keyMatcher, valueMatcher = null;
|
||||
|
||||
if (values == null) {
|
||||
valueMatcher = AnyMatcher.getInstance();
|
||||
} else {
|
||||
valueList = new ArrayList<String>(Arrays.asList(SPLIT_PATTERN.split(values)));
|
||||
if (valueList.remove(STRING_NEGATION))
|
||||
negativeRule = true;
|
||||
else if (valueList.remove(STRING_EXCLUSIVE))
|
||||
exclusionRule = true;
|
||||
else {
|
||||
valueMatcher = getValueMatcher(valueList);
|
||||
valueMatcher = RuleOptimizer.optimize(valueMatcher, ruleStack);
|
||||
}
|
||||
}
|
||||
|
||||
if (keys == null) {
|
||||
if (negativeRule || exclusionRule) {
|
||||
throw new IllegalArgumentException("negative rule requires key");
|
||||
}
|
||||
keyMatcher = AnyMatcher.getInstance();
|
||||
} else {
|
||||
keyList = new ArrayList<String>(Arrays.asList(SPLIT_PATTERN.split(keys)));
|
||||
keyMatcher = getKeyMatcher(keyList);
|
||||
|
||||
if ((keyMatcher instanceof AnyMatcher) && (negativeRule || exclusionRule)) {
|
||||
throw new IllegalArgumentException("negative rule requires key");
|
||||
}
|
||||
|
||||
if (valueList.remove(STRING_EXCLUSIVE)) {
|
||||
AttributeMatcher attributeMatcher = new NegativeMatcher(keyList, valueList,
|
||||
true);
|
||||
return new NegativeRule(element, closed, zoom, attributeMatcher);
|
||||
if (negativeRule) {
|
||||
AttributeMatcher attributeMatcher = new NegativeMatcher(keyList, valueList, false);
|
||||
return new NegativeRule(element, zoom, attributeMatcher);
|
||||
} else if (exclusionRule) {
|
||||
AttributeMatcher attributeMatcher = new NegativeMatcher(keyList, valueList, true);
|
||||
return new NegativeRule(element, zoom, attributeMatcher);
|
||||
}
|
||||
|
||||
keyMatcher = RuleOptimizer.optimize(keyMatcher, ruleStack);
|
||||
}
|
||||
AttributeMatcher keyMatcher = getKeyMatcher(keyList);
|
||||
AttributeMatcher valueMatcher = getValueMatcher(valueList);
|
||||
|
||||
keyMatcher = RuleOptimizer.optimize(keyMatcher, ruleStack);
|
||||
valueMatcher = RuleOptimizer.optimize(valueMatcher, ruleStack);
|
||||
|
||||
return new PositiveRule(element, closed, zoom,
|
||||
keyMatcher, valueMatcher);
|
||||
return new PositiveRule(element, zoom, keyMatcher, valueMatcher);
|
||||
}
|
||||
|
||||
private static AttributeMatcher getKeyMatcher(List<String> keyList) {
|
||||
@ -107,15 +124,8 @@ public abstract class Rule {
|
||||
return attributeMatcher;
|
||||
}
|
||||
|
||||
private static void validate(String elementName, String keys, String values,
|
||||
byte zoomMin, byte zoomMax) {
|
||||
if (keys == null) {
|
||||
throw new IllegalArgumentException("missing attribute k for element: "
|
||||
+ elementName);
|
||||
} else if (values == null) {
|
||||
throw new IllegalArgumentException("missing attribute v for element: "
|
||||
+ elementName);
|
||||
} else if (zoomMin < 0) {
|
||||
private static void validate(byte zoomMin, byte zoomMax) {
|
||||
if (zoomMin < 0) {
|
||||
throw new IllegalArgumentException("zoom-min must not be negative: "
|
||||
+ zoomMin);
|
||||
} else if (zoomMax < 0) {
|
||||
@ -129,9 +139,9 @@ public abstract class Rule {
|
||||
|
||||
public static Rule create(String elementName, Attributes attributes, Stack<Rule> ruleStack) {
|
||||
int element = Element.ANY;
|
||||
int closed = Closed.ANY;
|
||||
String keys = null;
|
||||
String values = null;
|
||||
int closed = Closed.ANY;
|
||||
byte zoomMin = 0;
|
||||
byte zoomMax = Byte.MAX_VALUE;
|
||||
|
||||
@ -164,8 +174,13 @@ public abstract class Rule {
|
||||
}
|
||||
}
|
||||
|
||||
validate(elementName, keys, values, zoomMin, zoomMax);
|
||||
return createRule(ruleStack, element, keys, values, closed, zoomMin, zoomMax);
|
||||
if (closed == Closed.YES)
|
||||
element = Element.POLY;
|
||||
else if (closed == Closed.NO)
|
||||
element = Element.LINE;
|
||||
|
||||
validate(zoomMin, zoomMax);
|
||||
return createRule(ruleStack, element, keys, values, zoomMin, zoomMax);
|
||||
}
|
||||
|
||||
private ArrayList<RenderInstruction> mRenderInstructions;
|
||||
@ -176,12 +191,10 @@ public abstract class Rule {
|
||||
|
||||
final int mZoom;
|
||||
final int mElement;
|
||||
final int mClosed;
|
||||
|
||||
Rule(int element, int closed, int zoom) {
|
||||
Rule(int type, int zoom) {
|
||||
|
||||
mClosed = closed;
|
||||
mElement = element;
|
||||
mElement = type;
|
||||
mZoom = zoom;
|
||||
|
||||
mRenderInstructions = new ArrayList<RenderInstruction>(4);
|
||||
@ -196,32 +209,12 @@ public abstract class Rule {
|
||||
mSubRules.add(rule);
|
||||
}
|
||||
|
||||
abstract boolean matchesNode(Tag[] tags);
|
||||
abstract boolean matchesTags(Tag[] tags);
|
||||
|
||||
abstract boolean matchesWay(Tag[] tags);
|
||||
|
||||
public void matchNode(Tag[] tags, int zoomLevel,
|
||||
public void matchElement(int type, Tag[] tags, int zoomLevel,
|
||||
List<RenderInstruction> matchingList) {
|
||||
if (((mElement & Element.NODE) != 0)
|
||||
&& ((mZoom & zoomLevel) != 0)
|
||||
&& matchesNode(tags)) {
|
||||
|
||||
for (int i = 0, n = mRenderInstructionArray.length; i < n; i++)
|
||||
matchingList.add(mRenderInstructionArray[i]);
|
||||
|
||||
for (int i = 0, n = mSubRuleArray.length; i < n; i++)
|
||||
mSubRuleArray[i].matchNode(tags, zoomLevel, matchingList);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public void matchWay(Tag[] tags, int zoomLevel,
|
||||
int closed, List<RenderInstruction> matchingList) {
|
||||
|
||||
if (((mElement & Element.WAY) != 0)
|
||||
&& ((mZoom & zoomLevel) != 0)
|
||||
&& ((mClosed & closed) != 0)
|
||||
&& (matchesWay(tags))) {
|
||||
if (((mElement & type) != 0) && ((mZoom & zoomLevel) != 0) && (matchesTags(tags))) {
|
||||
|
||||
// add instructions for this rule
|
||||
for (RenderInstruction ri : mRenderInstructionArray)
|
||||
@ -229,7 +222,7 @@ public abstract class Rule {
|
||||
|
||||
// check subrules
|
||||
for (Rule subRule : mSubRuleArray)
|
||||
subRule.matchWay(tags, zoomLevel, closed, matchingList);
|
||||
subRule.matchElement(type, tags, zoomLevel, matchingList);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user