/** Copyright 2015 Tim Engler, Rareventure LLC This file is part of Tiny Travel Tracker. Tiny Travel Tracker is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Tiny Travel Tracker 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. You should have received a copy of the GNU General Public License along with Tiny Travel Tracker. If not, see <http://www.gnu.org/licenses/>. */ package com.rareventure.gps2.reviewer.map.sas; import java.util.ArrayList; import java.util.TreeSet; import android.util.Log; import com.rareventure.gps2.CacheException; import com.rareventure.gps2.GTG; import com.rareventure.gps2.database.cache.AreaPanel; import com.rareventure.gps2.database.cache.AreaPanelCache; import com.rareventure.gps2.database.cache.TimeTree; public class Area { private static final int MIN_AP_DIVISIONS_FOR_AREA = 32; @Override public String toString() { return "Area [x1=" + x1 + ", y1=" + y1 + ", x2=" + x2 + ", y2=" + y2 + ", minDepth=" + minDepth + ", apiList=" + apiList + "]"; } public void addResetApisToTree(TreeSet<AreaPanelInfo> sortedApiTree, int startTime, int endTime) { for (AreaPanelInfo api : apiList) { api.resetToStart(startTime, endTime); if (!api.isExhausted()) sortedApiTree.add(api); } } public int x1, y1, x2, y2; public int minDepth; public ArrayList<AreaPanelInfo> apiList = new ArrayList<AreaPanelInfo>(); private int index; /** * @param minDepth the depth to align x1, x2, y1, and y2 */ public Area(int x1, int y1, int x2, int y2, int minDepth) { super(); // adjust the start and end to be on a good areapanel boundary this.minDepth = minDepth; this.x1 = AreaPanel.alignToDepth(x1, minDepth); this.y1 = AreaPanel.alignToDepth(y1, minDepth); // make x2 and y2 at least one ap unit away from x1 this.x2 = Math.max(AreaPanel.alignToDepth(x2, minDepth), x1 + AreaPanel.DEPTH_TO_WIDTH[minDepth]); this.y2 = Math.max(AreaPanel.alignToDepth(y2, minDepth), y1 + AreaPanel.DEPTH_TO_WIDTH[minDepth]); } public void setIndex(int index) { this.index = index; } public boolean encompasses(AreaPanel ap) { return x1 <= ap.getX() && y1 <= ap.getY() && x2 >= ap.getMaxX() && y2 >= ap.getMaxY(); } public void calcAreaPanelInfos() { if(!apiList.isEmpty()) return; // current path from root to area panel // note, we use integers so that if area panels get cached out and // replaced // it won't cause us a problem. In general, since we're read only, it // shouldn't // matter, but if the cache creator updates an area panel and the old // version is // floating around somewhere, it could cause a very hard to fix bug ArrayList<Integer> parentPath = new ArrayList<Integer>(); int lastSiblingApId = -1; if (GTG.apCache.isEmpty()) return; parentPath.add(AreaPanelCache.TOP_ROW_ID); // note that we get all of the area panels regardless of the start time // and end time // because there won't be many in any case (since we use bigger area // panels when // fully encompassed), and we won't need to a way to merge area panels // together // which were present for the previous and the current time when the // time range changes. while (!parentPath.isEmpty()) { AreaPanel ap = GTG.apCache.getRow(parentPath.get(parentPath .size() - 1)); /* ttt_installer:remove_line */Log.d(GTG.TAG, "Checking ap "+ap); if (encompasses(ap)) { apiList.add(new AreaPanelInfo(index, ap.id)); lastSiblingApId = parentPath.remove(parentPath.size() - 1); } else if (ap.getDepth() < minDepth) { throw new CacheException( "since we are rounding the area, all min depth ap's should be encompassed, got "+ap); } else { // go to children int nextChildIndex = lastSiblingApId == -1 ? 0 : ap .getIndexOfSubAreaPanelFk(lastSiblingApId) + 1; while (nextChildIndex < AreaPanel.NUM_SUB_PANELS && (ap.getSubAreaPanelFk(nextChildIndex) == Integer.MIN_VALUE || !ap .isRectOverlapsSubArea(x1, y1, x2, y2, nextChildIndex))) nextChildIndex++; // if there is another child to check if (nextChildIndex < AreaPanel.NUM_SUB_PANELS) { /* ttt_installer:remove_line */Log.d(GTG.TAG, "Found child"); parentPath.add(ap.getSubAreaPanelFk(nextChildIndex)); // if(!ap.getSubAreaPanel(nextChildIndex). // overlapsArea(this.x1,y1,x2,y2)) // { // ap // .isRectOverlapsSubArea(x1, y1, x2, y2, // nextChildIndex); // throw new IllegalArgumentException("remove me"); // } lastSiblingApId = -1; } else { /* ttt_installer:remove_line */Log.d(GTG.TAG, "No children left to check"); lastSiblingApId = parentPath .remove(parentPath.size() - 1); } } } } /** * This represents a particular area panel within an area and a particular * time tree of that area panel. It is used to find the exact times that a * path enters and leaves a particular area. */ public static class AreaPanelInfo implements Comparable<AreaPanelInfo> { public int apId, currTtId = -1, currTtStartTime = Integer.MAX_VALUE; /** * The index of the area that contains this API */ public int areaIndex; public AreaPanelInfo(int areaIndex, int apId) { this.areaIndex = areaIndex; this.apId = apId; } public TimeTree tt() { if(currTtId == -1) throw new CacheException("-1 tt: "+this); return GTG.ttCache.getRow(currTtId); } private AreaPanelInfo(int apId, int currTtId, int currTtStartTime, int areaIndex) { super(); this.apId = apId; this.currTtId = currTtId; this.currTtStartTime = currTtStartTime; this.areaIndex = areaIndex; } public AreaPanelInfo copy() { return new AreaPanelInfo(apId, currTtId, currTtStartTime, areaIndex); } public void resetToStart(int startTime, int endTime) { TimeTree tt = ap().getTimeTree(); setTt(tt.getEncompassigTimeTreeOrMinTimeTreeAfterTime(startTime, true), endTime); } public boolean isExhausted() { return currTtId == -1; } @Override public int compareTo(AreaPanelInfo another) { if (another.currTtStartTime == Integer.MAX_VALUE) throw new CacheException( "other area panel info is dead and shouldn't be compared " + another); if (currTtStartTime == Integer.MAX_VALUE) throw new CacheException( "area panel info is dead and shouldn't be compared " + this); return currTtStartTime - another.currTtStartTime; } public AreaPanel ap() { return GTG.apCache.getRow(apId); } /** * if the tt starts beyond endTime, sets to null also sets to null if tt * is null * * @param tt * @param endTime * @return */ public boolean setTt(TimeTree tt, int endTime) { if (tt == null || tt.getMinTimeSecs() >= endTime) { currTtId = -1; currTtStartTime = Integer.MAX_VALUE; return false; } currTtId = tt.id; currTtStartTime = tt.getMinTimeSecs(); return true; } @Override public String toString() { return "AreaPanelInfo [apId=" + apId + ", currTtId=" + currTtId + ", currTtStartTime=" + currTtStartTime + ", areaIndex=" + areaIndex + "]"; } } public int getCenterX() { return (x1 + x2) >> 1; } public int getCenterY() { return (y1 + y2) >> 1; } } // end of Area class