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.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.oscim.core.GeometryBuffer.GeometryType;
|
|
||||||
import org.oscim.core.MapElement;
|
import org.oscim.core.MapElement;
|
||||||
import org.oscim.theme.renderinstruction.RenderInstruction;
|
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.theme.rule.Rule;
|
||||||
import org.oscim.utils.LRUCache;
|
import org.oscim.utils.LRUCache;
|
||||||
import org.xml.sax.Attributes;
|
import org.xml.sax.Attributes;
|
||||||
@ -91,6 +90,7 @@ public class RenderTheme implements IRenderTheme {
|
|||||||
private Rule[] mRules;
|
private Rule[] mRules;
|
||||||
|
|
||||||
class ElementCache {
|
class ElementCache {
|
||||||
|
final int matchType;
|
||||||
final LRUCache<MatchingCacheKey, RenderInstructionItem> cache;
|
final LRUCache<MatchingCacheKey, RenderInstructionItem> cache;
|
||||||
final MatchingCacheKey cacheKey;
|
final MatchingCacheKey cacheKey;
|
||||||
|
|
||||||
@ -99,10 +99,11 @@ public class RenderTheme implements IRenderTheme {
|
|||||||
|
|
||||||
RenderInstructionItem prevItem;
|
RenderInstructionItem prevItem;
|
||||||
|
|
||||||
public ElementCache() {
|
public ElementCache(int type) {
|
||||||
cache = new LRUCache<MatchingCacheKey, RenderInstructionItem>(MATCHING_CACHE_SIZE);
|
cache = new LRUCache<MatchingCacheKey, RenderInstructionItem>(MATCHING_CACHE_SIZE);
|
||||||
instructionList = new ArrayList<RenderInstruction>(4);
|
instructionList = new ArrayList<RenderInstruction>(4);
|
||||||
cacheKey = new MatchingCacheKey();
|
cacheKey = new MatchingCacheKey();
|
||||||
|
matchType = type;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -121,9 +122,9 @@ public class RenderTheme implements IRenderTheme {
|
|||||||
mBaseTextSize = baseTextSize;
|
mBaseTextSize = baseTextSize;
|
||||||
|
|
||||||
mElementCache = new ElementCache[3];
|
mElementCache = new ElementCache[3];
|
||||||
for (int i = 0; i < 3; i++)
|
mElementCache[0] = new ElementCache(Element.NODE);
|
||||||
mElementCache[i] = new ElementCache();
|
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;
|
List<RenderInstruction> matches = cache.instructionList;
|
||||||
matches.clear();
|
matches.clear();
|
||||||
|
|
||||||
if (element.type == GeometryType.LINE) {
|
|
||||||
for (Rule rule : mRules)
|
for (Rule rule : mRules)
|
||||||
rule.matchWay(element.tags, zoomMask, Closed.NO, matches);
|
rule.matchElement(cache.matchType, element.tags, zoomMask, 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
int size = matches.size();
|
int size = matches.size();
|
||||||
if (size > 1) {
|
if (size > 1) {
|
||||||
|
|||||||
@ -117,12 +117,13 @@ public class RenderThemeHandler extends DefaultHandler {
|
|||||||
private Rule mCurrentRule;
|
private Rule mCurrentRule;
|
||||||
|
|
||||||
private final Stack<Element> mElementStack = new Stack<Element>();
|
private final Stack<Element> mElementStack = new Stack<Element>();
|
||||||
private int mLevel;
|
|
||||||
private RenderTheme mRenderTheme;
|
|
||||||
private final Stack<Rule> mRuleStack = new Stack<Rule>();
|
private final Stack<Rule> mRuleStack = new Stack<Rule>();
|
||||||
private final HashMap<String, RenderInstruction> tmpStyleHash =
|
private final HashMap<String, RenderInstruction> tmpStyleHash =
|
||||||
new HashMap<String, RenderInstruction>(10);
|
new HashMap<String, RenderInstruction>(10);
|
||||||
|
|
||||||
|
private int mLevel;
|
||||||
|
private RenderTheme mRenderTheme;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void endDocument() {
|
public void endDocument() {
|
||||||
if (mRenderTheme == null) {
|
if (mRenderTheme == null) {
|
||||||
@ -130,8 +131,11 @@ public class RenderThemeHandler extends DefaultHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
mRenderTheme.complete(mRulesList, mLevel);
|
mRenderTheme.complete(mRulesList, mLevel);
|
||||||
|
|
||||||
mRulesList.clear();
|
mRulesList.clear();
|
||||||
tmpStyleHash.clear();
|
tmpStyleHash.clear();
|
||||||
|
mRuleStack.clear();
|
||||||
|
mElementStack.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -153,7 +157,6 @@ public class RenderThemeHandler extends DefaultHandler {
|
|||||||
Log.d(TAG, exception.getMessage());
|
Log.d(TAG, exception.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void startElement(String uri, String localName, String qName,
|
public void startElement(String uri, String localName, String qName,
|
||||||
Attributes attributes) throws SAXException {
|
Attributes attributes) throws SAXException {
|
||||||
|
|||||||
@ -15,8 +15,10 @@
|
|||||||
*/
|
*/
|
||||||
package org.oscim.theme.rule;
|
package org.oscim.theme.rule;
|
||||||
|
|
||||||
final class Element {
|
public final class Element {
|
||||||
public static final int NODE = 1 << 0;
|
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;
|
public static final int ANY = NODE | WAY;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -21,6 +21,14 @@ import org.oscim.core.Tag;
|
|||||||
class NegativeMatcher implements AttributeMatcher {
|
class NegativeMatcher implements AttributeMatcher {
|
||||||
private final String[] mKeyList;
|
private final String[] mKeyList;
|
||||||
private final String[] mValueList;
|
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;
|
private final boolean mExclusive;
|
||||||
|
|
||||||
NegativeMatcher(List<String> keyList, List<String> valueList, boolean exclusive) {
|
NegativeMatcher(List<String> keyList, List<String> valueList, boolean exclusive) {
|
||||||
@ -42,25 +50,23 @@ class NegativeMatcher implements AttributeMatcher {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean matches(Tag[] tags) {
|
public boolean matches(Tag[] tags) {
|
||||||
if (keyListDoesNotContainKeys(tags)) {
|
if (keyListDoesNotContainKeys(tags))
|
||||||
return true;
|
return true;
|
||||||
}
|
|
||||||
|
|
||||||
for (Tag tag : tags) {
|
for (Tag tag : tags)
|
||||||
for (String value : mValueList)
|
for (String value : mValueList)
|
||||||
if (value == tag.value)
|
if (value == tag.value)
|
||||||
return !mExclusive;
|
return !mExclusive;
|
||||||
}
|
|
||||||
return mExclusive;
|
return mExclusive;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean keyListDoesNotContainKeys(Tag[] tags) {
|
private boolean keyListDoesNotContainKeys(Tag[] tags) {
|
||||||
for (Tag tag : tags) {
|
for (Tag tag : tags)
|
||||||
for (String key : mKeyList)
|
for (String key : mKeyList)
|
||||||
if (key == tag.key)
|
if (key == tag.key)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2010, 2011, 2012 mapsforge.org
|
* 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
|
* 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
|
* 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 {
|
class NegativeRule extends Rule {
|
||||||
final AttributeMatcher mAttributeMatcher;
|
final AttributeMatcher mAttributeMatcher;
|
||||||
|
|
||||||
NegativeRule(int element, int closed, int zoom,
|
NegativeRule(int element, int zoom, AttributeMatcher attributeMatcher) {
|
||||||
AttributeMatcher attributeMatcher) {
|
super(element, zoom);
|
||||||
super(element, closed, zoom);
|
|
||||||
|
|
||||||
mAttributeMatcher = attributeMatcher;
|
mAttributeMatcher = attributeMatcher;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
boolean matchesNode(Tag[] tags) {
|
boolean matchesTags(Tag[] tags) {
|
||||||
return mAttributeMatcher.matches(tags);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
boolean matchesWay(Tag[] tags) {
|
|
||||||
return mAttributeMatcher.matches(tags);
|
return mAttributeMatcher.matches(tags);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -20,9 +20,8 @@ class PositiveRule extends Rule {
|
|||||||
final AttributeMatcher mKeyMatcher;
|
final AttributeMatcher mKeyMatcher;
|
||||||
final AttributeMatcher mValueMatcher;
|
final AttributeMatcher mValueMatcher;
|
||||||
|
|
||||||
PositiveRule(int element, int closed, int zoom,
|
PositiveRule(int element, int zoom, AttributeMatcher keyMatcher, AttributeMatcher valueMatcher) {
|
||||||
AttributeMatcher keyMatcher, AttributeMatcher valueMatcher) {
|
super(element, zoom);
|
||||||
super(element, closed, zoom);
|
|
||||||
|
|
||||||
if (keyMatcher instanceof AnyMatcher)
|
if (keyMatcher instanceof AnyMatcher)
|
||||||
mKeyMatcher = null;
|
mKeyMatcher = null;
|
||||||
@ -36,14 +35,8 @@ class PositiveRule extends Rule {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
boolean matchesNode(Tag[] tags) {
|
boolean matchesTags(Tag[] tags) {
|
||||||
return (mKeyMatcher == null || mKeyMatcher.matches(tags))
|
return (mKeyMatcher == null || mKeyMatcher.matches(tags))
|
||||||
&& (mValueMatcher == null || mValueMatcher.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 final String STRING_WILDCARD = "*";
|
||||||
|
|
||||||
private static Rule createRule(Stack<Rule> ruleStack, int element, String keys,
|
private static Rule createRule(Stack<Rule> ruleStack, int element, String keys,
|
||||||
String values, int closed,
|
String values, byte zoomMin, byte zoomMax) {
|
||||||
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)));
|
|
||||||
|
|
||||||
int zoom = 0;
|
int zoom = 0;
|
||||||
for (int z = zoomMin; z <= zoomMax && z < 32; z++)
|
for (int z = zoomMin; z <= zoomMax && z < 32; z++)
|
||||||
zoom |= (1 << z);
|
zoom |= (1 << z);
|
||||||
|
|
||||||
if (valueList.remove(STRING_NEGATION)) {
|
List<String> keyList = null, valueList = null;
|
||||||
AttributeMatcher attributeMatcher = new NegativeMatcher(keyList, valueList,
|
boolean negativeRule = false;
|
||||||
false);
|
boolean exclusionRule = false;
|
||||||
return new NegativeRule(element, closed, zoom,
|
|
||||||
attributeMatcher);
|
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) {
|
||||||
if (valueList.remove(STRING_EXCLUSIVE)) {
|
throw new IllegalArgumentException("negative rule requires key");
|
||||||
AttributeMatcher attributeMatcher = new NegativeMatcher(keyList, valueList,
|
}
|
||||||
true);
|
keyMatcher = AnyMatcher.getInstance();
|
||||||
return new NegativeRule(element, closed, zoom, attributeMatcher);
|
} 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 (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);
|
||||||
}
|
}
|
||||||
AttributeMatcher keyMatcher = getKeyMatcher(keyList);
|
|
||||||
AttributeMatcher valueMatcher = getValueMatcher(valueList);
|
|
||||||
|
|
||||||
keyMatcher = RuleOptimizer.optimize(keyMatcher, ruleStack);
|
keyMatcher = RuleOptimizer.optimize(keyMatcher, ruleStack);
|
||||||
valueMatcher = RuleOptimizer.optimize(valueMatcher, ruleStack);
|
}
|
||||||
|
|
||||||
return new PositiveRule(element, closed, zoom,
|
return new PositiveRule(element, zoom, keyMatcher, valueMatcher);
|
||||||
keyMatcher, valueMatcher);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static AttributeMatcher getKeyMatcher(List<String> keyList) {
|
private static AttributeMatcher getKeyMatcher(List<String> keyList) {
|
||||||
@ -107,15 +124,8 @@ public abstract class Rule {
|
|||||||
return attributeMatcher;
|
return attributeMatcher;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void validate(String elementName, String keys, String values,
|
private static void validate(byte zoomMin, byte zoomMax) {
|
||||||
byte zoomMin, byte zoomMax) {
|
if (zoomMin < 0) {
|
||||||
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) {
|
|
||||||
throw new IllegalArgumentException("zoom-min must not be negative: "
|
throw new IllegalArgumentException("zoom-min must not be negative: "
|
||||||
+ zoomMin);
|
+ zoomMin);
|
||||||
} else if (zoomMax < 0) {
|
} else if (zoomMax < 0) {
|
||||||
@ -129,9 +139,9 @@ public abstract class Rule {
|
|||||||
|
|
||||||
public static Rule create(String elementName, Attributes attributes, Stack<Rule> ruleStack) {
|
public static Rule create(String elementName, Attributes attributes, Stack<Rule> ruleStack) {
|
||||||
int element = Element.ANY;
|
int element = Element.ANY;
|
||||||
|
int closed = Closed.ANY;
|
||||||
String keys = null;
|
String keys = null;
|
||||||
String values = null;
|
String values = null;
|
||||||
int closed = Closed.ANY;
|
|
||||||
byte zoomMin = 0;
|
byte zoomMin = 0;
|
||||||
byte zoomMax = Byte.MAX_VALUE;
|
byte zoomMax = Byte.MAX_VALUE;
|
||||||
|
|
||||||
@ -164,8 +174,13 @@ public abstract class Rule {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
validate(elementName, keys, values, zoomMin, zoomMax);
|
if (closed == Closed.YES)
|
||||||
return createRule(ruleStack, element, keys, values, closed, zoomMin, zoomMax);
|
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;
|
private ArrayList<RenderInstruction> mRenderInstructions;
|
||||||
@ -176,12 +191,10 @@ public abstract class Rule {
|
|||||||
|
|
||||||
final int mZoom;
|
final int mZoom;
|
||||||
final int mElement;
|
final int mElement;
|
||||||
final int mClosed;
|
|
||||||
|
|
||||||
Rule(int element, int closed, int zoom) {
|
Rule(int type, int zoom) {
|
||||||
|
|
||||||
mClosed = closed;
|
mElement = type;
|
||||||
mElement = element;
|
|
||||||
mZoom = zoom;
|
mZoom = zoom;
|
||||||
|
|
||||||
mRenderInstructions = new ArrayList<RenderInstruction>(4);
|
mRenderInstructions = new ArrayList<RenderInstruction>(4);
|
||||||
@ -196,32 +209,12 @@ public abstract class Rule {
|
|||||||
mSubRules.add(rule);
|
mSubRules.add(rule);
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract boolean matchesNode(Tag[] tags);
|
abstract boolean matchesTags(Tag[] tags);
|
||||||
|
|
||||||
abstract boolean matchesWay(Tag[] tags);
|
public void matchElement(int type, Tag[] tags, int zoomLevel,
|
||||||
|
|
||||||
public void matchNode(Tag[] tags, int zoomLevel,
|
|
||||||
List<RenderInstruction> matchingList) {
|
List<RenderInstruction> matchingList) {
|
||||||
if (((mElement & Element.NODE) != 0)
|
|
||||||
&& ((mZoom & zoomLevel) != 0)
|
|
||||||
&& matchesNode(tags)) {
|
|
||||||
|
|
||||||
for (int i = 0, n = mRenderInstructionArray.length; i < n; i++)
|
if (((mElement & type) != 0) && ((mZoom & zoomLevel) != 0) && (matchesTags(tags))) {
|
||||||
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))) {
|
|
||||||
|
|
||||||
// add instructions for this rule
|
// add instructions for this rule
|
||||||
for (RenderInstruction ri : mRenderInstructionArray)
|
for (RenderInstruction ri : mRenderInstructionArray)
|
||||||
@ -229,7 +222,7 @@ public abstract class Rule {
|
|||||||
|
|
||||||
// check subrules
|
// check subrules
|
||||||
for (Rule subRule : mSubRuleArray)
|
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