/*
* Copyright 2010, 2011, 2012 mapsforge.org
*
* 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
* Foundation, either version 3 of the License, or (at your option) any later version.
*
* 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.mapsforge.map.rendertheme.rule;
import java.util.ArrayList;
import java.util.List;
import org.mapsforge.core.model.Tag;
import org.mapsforge.core.util.LRUCache;
import org.mapsforge.map.rendertheme.RenderCallback;
import org.mapsforge.map.rendertheme.renderinstruction.RenderInstruction;
/**
* A RenderTheme defines how ways and nodes are drawn.
*/
public class RenderTheme {
private static final int MATCHING_CACHE_SIZE = 512;
private final float baseStrokeWidth;
private final float baseTextSize;
private int levels;
private final int mapBackground;
private final LRUCache<MatchingCacheKey, List<RenderInstruction>> matchingCache;
private final ArrayList<Rule> rulesList;
RenderTheme(RenderThemeBuilder renderThemeBuilder) {
this.baseStrokeWidth = renderThemeBuilder.baseStrokeWidth;
this.baseTextSize = renderThemeBuilder.baseTextSize;
this.mapBackground = renderThemeBuilder.mapBackground;
this.rulesList = new ArrayList<Rule>();
this.matchingCache = new LRUCache<MatchingCacheKey, List<RenderInstruction>>(MATCHING_CACHE_SIZE);
}
/**
* Must be called when this RenderTheme gets destroyed to clean up and free resources.
*/
public void destroy() {
this.matchingCache.clear();
for (int i = 0, n = this.rulesList.size(); i < n; ++i) {
this.rulesList.get(i).onDestroy();
}
}
/**
* @return the number of distinct drawing levels required by this RenderTheme.
*/
public int getLevels() {
return this.levels;
}
/**
* @return the map background color of this RenderTheme.
*/
public int getMapBackground() {
return this.mapBackground;
}
/**
* Matches a closed way with the given parameters against this RenderTheme.
*
* @param renderCallback
* the callback implementation which will be executed on each match.
* @param tags
* the tags of the way.
* @param zoomLevel
* the zoom level at which the way should be matched.
*/
public void matchClosedWay(RenderCallback renderCallback, List<Tag> tags, byte zoomLevel) {
matchWay(renderCallback, tags, zoomLevel, Closed.YES);
}
/**
* Matches a linear way with the given parameters against this RenderTheme.
*
* @param renderCallback
* the callback implementation which will be executed on each match.
* @param tags
* the tags of the way.
* @param zoomLevel
* the zoom level at which the way should be matched.
*/
public void matchLinearWay(RenderCallback renderCallback, List<Tag> tags, byte zoomLevel) {
matchWay(renderCallback, tags, zoomLevel, Closed.NO);
}
/**
* Matches a node with the given parameters against this RenderTheme.
*
* @param renderCallback
* the callback implementation which will be executed on each match.
* @param tags
* the tags of the node.
* @param zoomLevel
* the zoom level at which the node should be matched.
*/
public void matchNode(RenderCallback renderCallback, List<Tag> tags, byte zoomLevel) {
for (int i = 0, n = this.rulesList.size(); i < n; ++i) {
this.rulesList.get(i).matchNode(renderCallback, tags, zoomLevel);
}
}
/**
* Scales the stroke width of this RenderTheme by the given factor.
*
* @param scaleFactor
* the factor by which the stroke width should be scaled.
*/
public void scaleStrokeWidth(float scaleFactor) {
for (int i = 0, n = this.rulesList.size(); i < n; ++i) {
this.rulesList.get(i).scaleStrokeWidth(scaleFactor * this.baseStrokeWidth);
}
}
/**
* Scales the text size of this RenderTheme by the given factor.
*
* @param scaleFactor
* the factor by which the text size should be scaled.
*/
public void scaleTextSize(float scaleFactor) {
for (int i = 0, n = this.rulesList.size(); i < n; ++i) {
this.rulesList.get(i).scaleTextSize(scaleFactor * this.baseTextSize);
}
}
private void matchWay(RenderCallback renderCallback, List<Tag> tags, byte zoomLevel, Closed closed) {
MatchingCacheKey matchingCacheKey = new MatchingCacheKey(tags, zoomLevel, closed);
List<RenderInstruction> matchingList = this.matchingCache.get(matchingCacheKey);
if (matchingList != null) {
// cache hit
for (int i = 0, n = matchingList.size(); i < n; ++i) {
matchingList.get(i).renderWay(renderCallback, tags);
}
return;
}
// cache miss
matchingList = new ArrayList<RenderInstruction>();
for (int i = 0, n = this.rulesList.size(); i < n; ++i) {
this.rulesList.get(i).matchWay(renderCallback, tags, zoomLevel, closed, matchingList);
}
this.matchingCache.put(matchingCacheKey, matchingList);
}
void addRule(Rule rule) {
this.rulesList.add(rule);
}
void complete() {
this.rulesList.trimToSize();
for (int i = 0, n = this.rulesList.size(); i < n; ++i) {
this.rulesList.get(i).onComplete();
}
}
void setLevels(int levels) {
this.levels = levels;
}
}