/** * Matches a 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. * @param closed way is Closed * @param render ... * @return currently processed render instructions */ public RenderInstruction[] matchWay( IRenderCallback renderCallback, Tag[] tags, byte zoomLevel, boolean closed, boolean render) { // list of renderinsctruction items in cache RenderInstructionItem ris = null; RenderInstructionItem prevInstructions = null; // the item matching tags and zoomlevel RenderInstructionItem ri = null; // temporary matching instructions list List<RenderInstruction> matches; LRUCache<MatchingCacheKey, RenderInstructionItem> matchingCache; MatchingCacheKey cacheKey; if (closed) matchingCache = mAreaCache; else matchingCache = mWayCache; int zoomMask = 1 << zoomLevel; synchronized (matchingCache) { if (closed) { cacheKey = mAreaCacheKey; matches = mAreaInstructionList; prevInstructions = mPrevAreaItem; } else { cacheKey = mWayCacheKey; matches = mWayInstructionList; prevInstructions = mPrevWayItem; } if (prevInstructions == null || (prevInstructions.zoom & zoomMask) == 0) { // previous instructions zoom does not match cacheKey.set(tags, null); } else { // compare if tags match previous instructions if (cacheKey.set(tags, prevInstructions.key)) { // Log.d(TAG, "same as previous " + // Arrays.deepToString(tags)); ri = prevInstructions; } } if (ri == null) { ris = matchingCache.get(cacheKey); for (ri = ris; ri != null; ri = ri.next) if ((ri.zoom & zoomMask) != 0) // cache hit break; } if (ri == null) { // cache miss // Log.d(TAG, missCnt++ + " / " + hitCnt + " Cache Miss"); int c = (closed ? Closed.YES : Closed.NO); // List<RenderInstruction> matches = mMatchingList; matches.clear(); for (int i = 0, n = mRulesList.size(); i < n; ++i) mRulesList.get(i).matchWay(renderCallback, tags, zoomLevel, c, matches); int size = matches.size(); // check if same instructions are used in another level for (ri = ris; ri != null; ri = ri.next) { if (size == 0) { if (ri.list != null) continue; // both matchinglists are empty break; } if (ri.list == null) continue; if (ri.list.length != size) continue; int i = 0; for (RenderInstruction r : ri.list) { if (r != matches.get(i)) break; i++; } if (i == size) // both matching lists contain the same items break; } if (ri != null) { // we found a same matchting list on another zoomlevel ri.zoom |= zoomMask; // Log.d(TAG, // zoomLevel + " same instructions " + size + " " // + Arrays.deepToString(tags)); } else { // Log.d(TAG, // zoomLevel + " new instructions " + size + " " // + Arrays.deepToString(tags)); ri = new RenderInstructionItem(); ri.zoom = zoomMask; if (size > 0) { ri.list = new RenderInstruction[size]; matches.toArray(ri.list); } // attach this list to the one found for MatchingKey if (ris != null) { ri.next = ris.next; ri.key = ris.key; ris.next = ri; } else { ri.key = new MatchingCacheKey(cacheKey); matchingCache.put(ri.key, ri); } } } if (closed) mPrevAreaItem = ri; else mPrevWayItem = ri; } if (render && ri.list != null) { for (int i = 0, n = ri.list.length; i < n; i++) ri.list[i].renderWay(renderCallback, tags); } return ri.list; }
/** * @param renderCallback ... * @param tags ... * @param zoomLevel ... * @return ... */ public RenderInstruction[] matchNode(IRenderCallback renderCallback, Tag[] tags, byte zoomLevel) { // list of renderinsctruction items in cache RenderInstructionItem ris = null; // the item matching tags and zoomlevel RenderInstructionItem ri = null; synchronized (mNodesCache) { int zoomMask = 1 << zoomLevel; MatchingCacheKey cacheKey = mNodeCacheKey; if (mPrevNodeItem == null || (mPrevNodeItem.zoom & zoomMask) == 0) { // previous instructions zoom does not match cacheKey.set(tags, null); } else { // compare if tags match previous instructions if (cacheKey.set(tags, mPrevNodeItem.key)) ri = mPrevNodeItem; } if (ri == null) { boolean found = mNodesCache.containsKey(cacheKey); if (found) { ris = mNodesCache.get(cacheKey); for (ri = ris; ri != null; ri = ri.next) if ((ri.zoom & zoomMask) != 0) // cache hit break; } } if (ri == null) { // cache miss List<RenderInstruction> matches = mNodeInstructionList; matches.clear(); for (int i = 0, n = mRulesList.size(); i < n; ++i) mRulesList.get(i).matchNode(renderCallback, tags, zoomLevel, matches); int size = matches.size(); // check if same instructions are used in another level for (ri = ris; ri != null; ri = ri.next) { if (size == 0) { if (ri.list != null) continue; // both matchinglists are empty break; } if (ri.list == null) continue; if (ri.list.length != size) continue; int i = 0; for (RenderInstruction r : ri.list) { if (r != matches.get(i)) break; i++; } if (i == size) // both matching lists contain the same items break; } if (ri != null) { // we found a same matchting list on another zoomlevel ri.zoom |= zoomMask; } else { ri = new RenderInstructionItem(); ri.zoom = zoomMask; if (size > 0) { ri.list = new RenderInstruction[size]; matches.toArray(ri.list); } // attach this list to the one found for MatchingKey if (ris != null) { ri.next = ris.next; ri.key = ris.key; ris.next = ri; } else { ri.key = new MatchingCacheKey(cacheKey); mNodesCache.put(ri.key, ri); } } } } if (ri.list != null) render(renderCallback, ri.list, tags); mPrevNodeItem = ri; return ri.list; }