@Override
  public List<STPoint> getSequence(STPoint start, STPoint end) {

    STPoint boundValues =
        new STPoint(Constants.PoK.X_RADIUS, Constants.PoK.Y_RADIUS, Constants.PoK.T_RADIUS);

    STPoint startNN = this.nearestNeighbor(start, boundValues, 1).get(0);
    STPoint endNN = this.nearestNeighbor(end, boundValues, 1).get(0);

    float minT = startNN.getT() > endNN.getT() ? startNN.getT() : endNN.getT();
    float maxT = startNN.getT() > endNN.getT() ? startNN.getT() : endNN.getT();
    //
    //        String query = "SELECT id,minX,maxX,minY,maxY,minT,maxT from " +
    // this.table_identifier;
    //        query += "WHERE minT<=" + maxT + " AND minT>=" + minT;
    //        query += "ORDER BY minT ASC";
    //
    //        SQLiteDatabase db = this.getReadableDatabase();
    STPoint minPoint = new STPoint();
    STPoint maxPoint = new STPoint();

    minPoint.setT(minT);
    maxPoint.setT(maxT);
    return this.range(new STRegion(minPoint, maxPoint));
  }
  @Override
  public STRegion getBoundingBox() {
    // TODO: get bounding box through R-Tree shadow tables

    SQLiteDatabase db = this.getReadableDatabase();

    String query = "SELECT minX FROM " + this.table_identifier + " ORDER BY minX ASC LIMIT 1;";
    float minX = getRowValueHelper(db, query, 0);
    query = "SELECT minY FROM " + this.table_identifier + " ORDER BY minY ASC Limit 1;";
    float minY = getRowValueHelper(db, query, 0);
    query = "SELECT minT FROM " + this.table_identifier + " ORDER BY minT ASC Limit 1;";
    float minT = getRowValueHelper(db, query, 0);
    query = "SELECT maxX FROM " + this.table_identifier + " ORDER BY maxX DESC Limit 1;";
    float maxX = getRowValueHelper(db, query, 0);
    query = "SELECT maxY FROM " + this.table_identifier + " ORDER BY maxY DESC Limit 1;";
    float maxY = getRowValueHelper(db, query, 0);
    query = "SELECT maxT FROM " + this.table_identifier + " ORDER BY maxT DESC Limit 1;";
    float maxT = getRowValueHelper(db, query, 0);

    STPoint mins = new STPoint(minX, minY, minT);
    STPoint maxs = new STPoint(maxX, maxY, maxT);
    if (Constants.SPATIAL_TYPE == Constants.SpatialType.GPS) {
      STPoint.add(mins, Constants.NEG_FLOAT_BUDGE);
      STPoint.add(maxs, Constants.POS_FLOAT_BUDGE);
    }
    return new STRegion(mins, maxs);
  }
 @Override
 public void insert(STPoint point) {
   SQLiteDatabase db = this.getWritableDatabase();
   ContentValues values = new ContentValues();
   values.put("minX", point.getX());
   values.put("maxX", point.getX());
   values.put("minY", point.getY());
   values.put("maxY", point.getY());
   values.put("minT", point.getT());
   values.put("maxT", point.getT());
   db.insert(
       this.table_identifier, // table
       null, // nullColumnHack
       values); // key/value -> keys = column names/ values = column values
   db.close();
 }
  @Override
  public List<STPoint> nearestNeighbor(STPoint needle, STPoint boundValues, int n) {

    // TODO support more than one nearest neighbor
    if (n != 1) {
      throw new RuntimeException("Nearest Neighbor does not support N != 1");
    }

    List<STPoint> candPoints = new ArrayList<STPoint>();
    for (int i = 0; i < 20; i++) {
      try {
        STRegion miniRegion = GPSLib.getSpaceBoundQuick(needle, boundValues, SPATIAL_TYPE);
        candPoints = range(miniRegion);
        if (candPoints.size() >= n) {
          break;
        }
        int step = (int) Math.pow(i, 3);
        boundValues.setX(step * boundValues.getX());
        boundValues.setY(step * boundValues.getY());
        boundValues.setT(step * boundValues.getT());
      } catch (LSTFilterException e) {
        e.printStackTrace();
      }
    }

    STPoint minPoint = null;
    double minVal = Double.MAX_VALUE;
    for (STPoint point : candPoints) {
      // ToDo: optimize this since we use the same needle each time, some values should drop out of
      // dist calc.
      // ToDO: optimize by using squared values of distance instead of ones using sqrt (expensive)
      try {
        double distance = GPSLib.distanceBetween(needle, point, SPATIAL_TYPE);
        if (distance < minVal) {
          minVal = distance;
          minPoint = point;
        }
      } catch (LSTFilterException e) {
        e.printStackTrace();
      }
    }

    List<STPoint> minPoints = new ArrayList<STPoint>();
    minPoints.add(minPoint);
    return minPoints;
  }
  @Override
  public List<STPoint> range(STRegion range) {
    SQLiteDatabase db = this.getReadableDatabase();

    String query = "SELECT id,minX,maxX,minY,maxY,minT,maxT from " + this.table_identifier + " ";

    STPoint mins = range.getMins();
    STPoint maxs = range.getMaxs();

    String prefix = " WHERE ";
    if (mins.hasX() && maxs.hasX()) {
      query +=
          prefix
              + " minX >= "
              + String.valueOf(mins.getX())
              + " AND maxX <= "
              + String.valueOf(maxs.getX());
      prefix = " AND ";
    }

    if (mins.hasY() && maxs.hasY()) {
      query +=
          prefix
              + " minY >= "
              + String.valueOf(mins.getY())
              + " AND maxY <= "
              + String.valueOf(maxs.getY());
      prefix = " AND ";
    }

    if (mins.hasT() && maxs.hasT()) {
      query +=
          prefix
              + " minT >= "
              + String.valueOf(mins.getT())
              + " AND maxT <= "
              + String.valueOf(maxs.getT());
      prefix = " AND ";
    }

    Cursor cur = db.rawQuery(query, null);

    List<STPoint> points = cursorToList(cur);
    db.close();
    return points;
  }