private static void paintArrowsInGraph(
     Canvas canvas,
     SpotWindData spotWindData,
     Images images,
     int startX,
     int endX,
     int xSteps,
     int startY,
     int endY,
     int stepXCount) {
   Paint paint = new Paint();
   int hourPos = 0;
   for (int x = startX; x <= endX; x += xSteps) {
     canvas.drawLine(x, startY, x, endY, paint);
     WindData windData = spotWindData.getWindData(hourPos);
     if (windData != null) {
       Matrix arrowImageMatrix = new Matrix();
       float origArrowSize = images.getArrowBitmap().getHeight();
       int arrowSize = 30;
       float arrowScale = arrowSize / origArrowSize;
       int rotation = windData.getAngle();
       arrowImageMatrix.postScale(arrowScale, arrowScale);
       arrowImageMatrix.postRotate(rotation, arrowSize / 2, arrowSize / 2);
       arrowImageMatrix.postTranslate(x - arrowSize / 2, startY + 5);
       canvas.drawBitmap(images.getArrowBitmap(), arrowImageMatrix, paint);
     }
     hourPos += 24 / stepXCount;
   }
 }
  private static void paintImageText(
      Context context,
      WidgetLayoutDetails widgetLayoutDetails,
      Canvas canvas,
      WindData windData,
      int xOffset,
      int yOffset) {
    int xPosText = xOffset + (int) widgetLayoutDetails.spotImageHeight + 30;
    int yPosTextTime = yOffset + (int) (widgetLayoutDetails.spotImageHeight / 4) * 1;
    int yPosTextWind = yOffset + (int) (widgetLayoutDetails.spotImageHeight / 4) * 2;
    int yPosTextAngle = yOffset + (int) (widgetLayoutDetails.spotImageHeight / 4) * 3;

    // paint text data
    Paint textPaint = new Paint();
    textPaint.setColor(Color.DKGRAY);
    textPaint.setTextSize(30);

    String timeStr = context.getResources().getString(R.string.graphics_no_data);
    String windStr = context.getResources().getString(R.string.graphics_no_data);
    String angleStr = context.getResources().getString(R.string.graphics_no_data);
    if (windData != null) {
      int wind = (int) Math.round(windData.getWind());
      int gust = (int) Math.round(windData.getGust());
      timeStr = windData.getTimeStr();
      windStr = wind + " - " + gust + " " + context.getResources().getString(R.string.graphics_mps);
      angleStr =
          windData.getAngle() + " " + context.getResources().getString(R.string.graphics_degrees);
    }

    canvas.drawText(timeStr, xPosText, yPosTextTime, textPaint);
    canvas.drawText(windStr, xPosText, yPosTextWind, textPaint);
    canvas.drawText(angleStr, xPosText, yPosTextAngle, textPaint);
  }
  private static void paintArrow(
      Canvas canvas,
      WidgetLayoutDetails widgetLayoutDetails,
      Images images,
      WindData windData,
      int xOffset,
      int yOffset) {
    Paint paint = new Paint();
    Matrix arrowImageMatrix = new Matrix();
    float origArrowSize = images.getArrowBitmap().getHeight();
    float arrowScale = widgetLayoutDetails.spotImageHeight / origArrowSize;
    float scaleForWind = (float) windData.getWind() / MAXWIND;
    if (scaleForWind > 1) scaleForWind = 1;
    int rotation = windData.getAngle();
    double arrowHeight = widgetLayoutDetails.spotImageHeight * scaleForWind;
    int offset = ((int) widgetLayoutDetails.spotImageHeight - (int) arrowHeight) / 2;

    arrowImageMatrix.postScale(arrowScale, arrowScale);
    arrowImageMatrix.postRotate(
        rotation, widgetLayoutDetails.spotImageHeight / 2, widgetLayoutDetails.spotImageHeight / 2);
    arrowImageMatrix.postScale(scaleForWind, scaleForWind);
    arrowImageMatrix.postTranslate(offset, offset);
    arrowImageMatrix.postTranslate(xOffset, yOffset);
    canvas.drawBitmap(images.getArrowBitmap(), arrowImageMatrix, paint);
  }
 public void InsertWindStat(WindData windStat, DbHandle handle) {
   SQLiteDatabase db = handle.getDb();
   ContentValues cv = new ContentValues();
   cv.put(COL_SPOT, windStat.getSpotID());
   cv.put(COL_DAY, windStat.getDay());
   cv.put(COL_HOUR, windStat.getEndHour());
   cv.put(COL_MINUTE, windStat.getEndMinute());
   cv.put(COL_START_HOUR, windStat.getStartHour());
   cv.put(COL_START_MINUTE, windStat.getStartMinute());
   cv.put(COL_WIND, windStat.getWind());
   cv.put(COL_GUST, windStat.getGust());
   cv.put(COL_ANGLE, windStat.getAngle());
   cv.put(COL_LAST_MODIFIED, windStat.getLastModified());
   db.insert(WINDTABLE, null, cv);
 }
 public void UpdateWindStat(WindData windStat, int id, DbHandle handle) {
   SQLiteDatabase db = handle.getDb();
   ContentValues cv = new ContentValues();
   cv.put(COL_DAY, windStat.getDay());
   cv.put(COL_SPOT, windStat.getSpotID());
   cv.put(COL_HOUR, windStat.getEndHour());
   cv.put(COL_MINUTE, windStat.getEndMinute());
   cv.put(COL_START_HOUR, windStat.getStartHour());
   cv.put(COL_START_MINUTE, windStat.getStartMinute());
   cv.put(COL_WIND, windStat.getWind());
   cv.put(COL_GUST, windStat.getGust());
   cv.put(COL_ANGLE, windStat.getAngle());
   cv.put(COL_LAST_MODIFIED, windStat.getLastModified());
   db.update(WINDTABLE, cv, COL_ID + "=?", new String[] {"" + id});
 }
  public static double calculateTouchedTime(
      float touchPos, int startX, int graphWidth, WindData firstWindData, WindData lastWindData) {
    double translatedPos = touchPos - startX;
    double time = 24 * translatedPos / graphWidth;
    if (time < 0) time = 0;
    if (time > 24) time = 24;

    if (firstWindData != null && time < firstWindData.getStartTime()) {
      time = firstWindData.getStartTime() + 0.02;
    }
    if (lastWindData != null && time > lastWindData.getEndTime()) {
      time = lastWindData.getEndTime() - 0.02;
    }
    return time;
  }
  public static Set<Bar> createGustGraph(
      SpotWindData spotDayData, int startX, int startY, int endX, int endY) {
    Set<Bar> path = new LinkedHashSet<Bar>();
    double xDiff = endX - startX;
    double yDiff = endY - startY;
    for (WindData windData : spotDayData.getWindDatas()) {
      double wind = windData.getWind();
      double gust = windData.getGust();
      if (wind > MAXWIND) wind = MAXWIND;
      if (gust > MAXWIND) gust = MAXWIND;
      double time2 = (double) windData.getEndHour() + (double) windData.getEndMinute() / 60;
      double time1 = (double) windData.getStartHour() + (double) windData.getStartMinute() / 60;
      double xProcent2 = time2 / 24;
      double yProcent2 = wind / MAXWIND;
      double yProcent1 = gust / MAXWIND;
      double xProcent1 = time1 / 24;
      double x1 = startX + xProcent1 * xDiff;
      double y1 = endY - yProcent1 * yDiff;
      double y2 = endY - yProcent2 * yDiff;
      double x2 = startX + xProcent2 * xDiff;
      if (x1 > x2) x1 = startX; // in case startTime was from the day before.

      Bar bar = new Bar();
      bar.setX1((int) x1);
      bar.setX2((int) x2);
      bar.setY1((int) y1);
      bar.setY2((int) y2);
      path.add(bar);
    }

    return path;
  }
  private static WIND_POWER calculateWindPower(Context context, WindData windData) {
    if (windData.getAngle() == -1 || windData.getWind() == -1 || windData.getGust() == -1) {
      return WIND_POWER.OUTDATED;
    }

    double wind = windData.getWind();
    double minWind = LocalStorage.getMinimalWind(context);
    double optimalWind = LocalStorage.getOptimalWind(context);
    double muchWind = LocalStorage.getMuchWind(context);
    double tooMuchWind = LocalStorage.getToomuchWind(context);

    if (wind <= minWind) return WIND_POWER.TOO_SHORT;
    if ((wind > minWind) && (wind <= optimalWind)) return WIND_POWER.OK;
    if ((wind > optimalWind) && (wind <= muchWind)) return WIND_POWER.GOOD;
    if ((wind > muchWind) && (wind <= tooMuchWind)) return WIND_POWER.WARNING;
    if (wind > tooMuchWind) return WIND_POWER.DANGER;
    return WIND_POWER.DANGER; // should never happen
  }
 public void insertOrUpdateWindStat(WindData windStat, DbHandle handle) {
   // find if row already exists
   Cursor cursor =
       getWindStatsOfDayAndTime(
           windStat.getSpotID(),
           windStat.getDay(),
           windStat.getEndHour(),
           windStat.getEndMinute(),
           handle);
   if (cursor.moveToFirst()) {
     // already exists, do an update
     int id = cursor.getInt(cursor.getColumnIndex(COL_ID));
     UpdateWindStat(windStat, id, handle);
   } else {
     // insert new row
     InsertWindStat(windStat, handle);
   }
   cursor.close();
 }
 private static WIND_DIRECTORION calculateWindAngleStatus(
     WindData windData, SpotWindData spotWindData) {
   int angleDiff = windData.getAngle() - spotWindData.getSpotData().getLocationDirection();
   // when angle diff < -180, then add 360
   if (angleDiff < -180) angleDiff += 360;
   // make absolute
   if (angleDiff < 0) angleDiff = angleDiff * -1;
   // check wind direction
   if (angleDiff <= 85) return WIND_DIRECTORION.AANLANDIG;
   if ((angleDiff > 85) && (angleDiff <= 95)) return WIND_DIRECTORION.SIDESHORE;
   if (angleDiff > 95) return WIND_DIRECTORION.AFLANDIG;
   return WIND_DIRECTORION.AFLANDIG; // should never happen
 }
 public WindData getWindStatFromCursor(Cursor cursor) {
   WindData windStat = new WindData();
   windStat.setSpotID(cursor.getInt(cursor.getColumnIndex(COL_SPOT)));
   windStat.setDay(cursor.getInt(cursor.getColumnIndex(COL_DAY)));
   windStat.setendHour(cursor.getInt(cursor.getColumnIndex(COL_HOUR)));
   windStat.setEndMinute(cursor.getInt(cursor.getColumnIndex(COL_MINUTE)));
   windStat.setStartHour(cursor.getInt(cursor.getColumnIndex(COL_START_HOUR)));
   windStat.setStartMinute(cursor.getInt(cursor.getColumnIndex(COL_START_MINUTE)));
   windStat.setWind(cursor.getFloat(cursor.getColumnIndex(COL_WIND)));
   windStat.setGust(cursor.getFloat(cursor.getColumnIndex(COL_GUST)));
   windStat.setAngle(cursor.getInt(cursor.getColumnIndex(COL_ANGLE)));
   windStat.setLastModified(cursor.getInt(cursor.getColumnIndex(COL_LAST_MODIFIED)));
   return windStat;
 }
  public static void paintCanvas(
      Context context,
      WidgetLayoutDetails widgetLayoutDetails,
      Canvas canvas,
      SpotWindData spotWindData,
      Set<Bar> windGraphPath,
      Set<Bar> gustGraphPath,
      float touchPos,
      boolean checkOutDated) {
    // define graph boundaries
    int startGraphX = 50;
    int startGraphY = widgetLayoutDetails.spotImageHeight + 10;
    int endGraphY = widgetLayoutDetails.widgetHeight - 30;
    int endGraphX = widgetLayoutDetails.widgetWidth - 30;
    int graphWidth = endGraphX - startGraphX;
    int yOffsetImage = widgetLayoutDetails.yOffsetImage;
    int xOffsetImage = widgetLayoutDetails.xOffsetImage;

    WindData lastWindData = spotWindData.getLastWindData();
    WindData firstWindData = spotWindData.getFirstWindData();

    // calculate the time that is touched (if no data is available, use the time most nearby)
    double time =
        calculateTouchedTime(touchPos, startGraphX, graphWidth, firstWindData, lastWindData);
    double selectedPos = (time * graphWidth) / 24 + startGraphX;

    // check preconditions
    if (spotWindData == null) return;
    if (spotWindData.getSpotData() == null) return;

    // get images
    Images images = ImageStorage.getImages(context);
    if (images.getArrowBitmap() == null) return;

    // load spot bitmap
    Bitmap spotBitmap = ImageStorage.getSpotImage(context, spotWindData.getSpotData().getSiteID());
    if (spotBitmap == null) return;

    // draw white background
    if (widgetLayoutDetails.drawWhiteBackground) {
      drawWhiteBackground(widgetLayoutDetails, canvas);
    }

    // draw graph
    if (widgetLayoutDetails.drawGraph) {
      int stepXCount = calculateXCount(widgetLayoutDetails);
      int stepYCount = calculateYCount(widgetLayoutDetails);
      int xSteps = calculateXSteps(endGraphX, startGraphX, stepXCount);
      int ySteps = calculateYSteps(endGraphY, startGraphY, stepYCount);
      // recalculate endX and endY with the new xSteps
      endGraphX = startGraphX + xSteps * stepXCount;
      endGraphY = startGraphY + ySteps * stepYCount;

      // paint time mark line
      if (widgetLayoutDetails.drawTimeline) {
        paintTimeMarkLine(canvas, endGraphX, startGraphX, widgetLayoutDetails, (float) selectedPos);
      }

      // paint raster
      paintRaster(
          canvas,
          startGraphX,
          endGraphX,
          xSteps,
          endGraphY,
          startGraphY,
          stepXCount,
          ySteps,
          stepYCount);

      // paint graph
      paintGraph(canvas, windGraphPath, gustGraphPath);

      // paint all arrows
      paintArrowsInGraph(
          canvas,
          spotWindData,
          images,
          startGraphX,
          endGraphX,
          xSteps,
          startGraphY,
          endGraphY,
          stepXCount);
    }

    // get the selected windData
    WindData windData = spotWindData.getWindData(time);

    // draw the image
    if (widgetLayoutDetails.drawImage) {
      // define advice location
      int adviceStatusIconTop = 10;
      int adviceStatusIconLeft =
          (int) widgetLayoutDetails.spotImageHeight
              - widgetLayoutDetails.windSpeedCircelDiameter
              - 10;

      // draw spot
      drawSpotImage(spotBitmap, widgetLayoutDetails, canvas, xOffsetImage, yOffsetImage);

      if (windData == null) {
        paintImageTextOutdated(context, widgetLayoutDetails, canvas, xOffsetImage, yOffsetImage);
        Bitmap globalAdviceBitmap = images.getUnknowmBitmap();
        paintAdviceIcon(
            canvas,
            widgetLayoutDetails.windSpeedCircelDiameter,
            globalAdviceBitmap,
            adviceStatusIconLeft,
            adviceStatusIconTop,
            xOffsetImage,
            yOffsetImage);
      }

      if (windData != null) {

        // find windData
        double windDataTime = windData.getEndHour() + (double) windData.getEndMinute() / 60;
        double currentTime = Util.calculateTime(new Date());
        double diffTime = (currentTime - windDataTime);
        boolean outDated = diffTime > 1;

        if (outDated && checkOutDated) {
          paintImageTextOutdated(context, widgetLayoutDetails, canvas, xOffsetImage, yOffsetImage);
          Bitmap globalAdviceBitmap = images.getUnknowmBitmap();
          paintAdviceIcon(
              canvas,
              widgetLayoutDetails.windSpeedCircelDiameter,
              globalAdviceBitmap,
              adviceStatusIconLeft,
              adviceStatusIconTop,
              xOffsetImage,
              yOffsetImage);
        } else {

          // paint the arrow
          paintArrow(canvas, widgetLayoutDetails, images, windData, xOffsetImage, yOffsetImage);

          // check aanlandig/sideshore/aflandig
          WIND_DIRECTORION windDirectorion = calculateWindAngleStatus(windData, spotWindData);
          WIND_POWER windPower = calculateWindPower(context, windData);
          int angleAdviceLevel = getAngleAdviceLevel(windDirectorion);
          int powerAdviceLevel = getWindPowerAdviceLevel(windPower);
          String windDateText = getAngleAdviceText(context, windDirectorion);
          int windPowerColor = getWindPowerColor(windPower);

          // define global advice
          Bitmap globalAdviceBitmap =
              getGlobalAdviceBitmap(images, angleAdviceLevel, powerAdviceLevel);

          // print circel for wind
          if (widgetLayoutDetails.drawWindInCorner) {
            int wind = (int) Math.round(windData.getWind());
            paintWindInCorner(
                canvas,
                widgetLayoutDetails.windSpeedCircelDiameter,
                wind,
                xOffsetImage,
                yOffsetImage);
          }

          paintAdviceIcon(
              canvas,
              widgetLayoutDetails.windSpeedCircelDiameter,
              globalAdviceBitmap,
              adviceStatusIconLeft,
              adviceStatusIconTop,
              xOffsetImage,
              yOffsetImage);

          if (widgetLayoutDetails.drawImageText) {
            paintImageText(
                context, widgetLayoutDetails, canvas, windData, xOffsetImage, yOffsetImage);
          }
        }
      }
    }
  }