private void drawPolygon(
      BinaryMapDataObject obj,
      RenderingRuleSearchRequest render,
      Canvas canvas,
      RenderingContext rc,
      TagValuePair pair) {
    if (render == null || pair == null) {
      return;
    }
    float xText = 0;
    float yText = 0;
    int zoom = rc.zoom;
    Path path = null;

    // rc.main.color = Color.rgb(245, 245, 245);
    render.setInitialTagValueZoom(pair.tag, pair.value, zoom, obj);
    boolean rendered = render.search(RenderingRulesStorage.POLYGON_RULES);
    if (!rendered || !updatePaint(render, paint, 0, true, rc)) {
      return;
    }
    rc.visible++;
    int len = obj.getPointsLength();
    for (int i = 0; i < obj.getPointsLength(); i++) {

      PointF p = calcPoint(obj, i, rc);
      xText += p.x;
      yText += p.y;
      if (path == null) {
        path = new Path();
        path.moveTo(p.x, p.y);
      } else {
        path.lineTo(p.x, p.y);
      }
    }
    int[][] polygonInnerCoordinates = obj.getPolygonInnerCoordinates();
    if (polygonInnerCoordinates != null && path != null) {
      path.setFillType(FillType.EVEN_ODD);
      for (int j = 0; j < polygonInnerCoordinates.length; j++) {
        for (int i = 0; i < polygonInnerCoordinates[j].length; i += 2) {
          PointF p =
              calcPoint(polygonInnerCoordinates[j][i], polygonInnerCoordinates[j][i + 1], rc);
          if (i == 0) {
            path.moveTo(p.x, p.y);
          } else {
            path.lineTo(p.x, p.y);
          }
        }
      }
    }

    if (path != null && len > 0) {
      canvas.drawPath(path, paint);
      if (updatePaint(render, paint, 1, false, rc)) {
        canvas.drawPath(path, paint);
      }
      textRenderer.renderText(obj, render, rc, pair, xText / len, yText / len, null, null);
    }
  }
  private void drawPoint(
      BinaryMapDataObject obj,
      RenderingRuleSearchRequest render,
      Canvas canvas,
      RenderingContext rc,
      TagValuePair pair,
      boolean renderText) {
    if (render == null || pair == null) {
      return;
    }
    render.setInitialTagValueZoom(pair.tag, pair.value, rc.zoom, obj);
    render.search(RenderingRulesStorage.POINT_RULES);

    String resId = render.getStringPropertyValue(render.ALL.R_ICON);
    if (resId == null && !renderText) {
      return;
    }
    int len = obj.getPointsLength();
    rc.visible++;
    PointF ps = new PointF(0, 0);
    for (int i = 0; i < len; i++) {
      PointF p = calcPoint(obj, i, rc);
      ps.x += p.x;
      ps.y += p.y;
    }
    if (len > 1) {
      ps.x /= len;
      ps.y /= len;
    }

    if (resId != null) {
      IconDrawInfo ico = new IconDrawInfo();
      ico.x = ps.x;
      ico.y = ps.y;
      ico.resId = resId;
      rc.iconsToDraw.add(ico);
    }
    if (renderText) {
      textRenderer.renderText(obj, render, rc, pair, ps.x, ps.y, null, null);
    }
  }
  private void drawPolyline(
      BinaryMapDataObject obj,
      RenderingRuleSearchRequest render,
      Canvas canvas,
      RenderingContext rc,
      TagValuePair pair,
      int layer,
      boolean drawOnlyShadow) {
    if (render == null || pair == null) {
      return;
    }
    int length = obj.getPointsLength();
    if (length < 2) {
      return;
    }
    render.setInitialTagValueZoom(pair.tag, pair.value, rc.zoom, obj);
    render.setIntFilter(render.ALL.R_LAYER, layer);
    boolean rendered = render.search(RenderingRulesStorage.LINE_RULES);
    if (!rendered || !updatePaint(render, paint, 0, false, rc)) {
      return;
    }
    int oneway = 0;
    if (rc.zoom >= 16 && "highway".equals(pair.tag)) { // $NON-NLS-1$
      if (obj.containsAdditionalType(obj.getMapIndex().onewayAttribute)) {
        oneway = 1;
      } else if (obj.containsAdditionalType(obj.getMapIndex().onewayReverseAttribute)) {
        oneway = -1;
      }
    }

    rc.visible++;

    Path path = null;
    float xMid = 0;
    float yMid = 0;
    int middle = obj.getPointsLength() / 2;
    PointF[] textPoints = null;
    if (!drawOnlyShadow) {
      textPoints = new PointF[length];
    }

    for (int i = 0; i < length; i++) {
      PointF p = calcPoint(obj, i, rc);
      if (textPoints != null) {
        textPoints[i] = new PointF(p.x, p.y);
      }
      if (path == null) {
        path = new Path();
        path.moveTo(p.x, p.y);
      } else {
        if (i == middle) {
          xMid = p.x;
          yMid = p.y;
        }
        path.lineTo(p.x, p.y);
      }
    }
    if (path != null) {
      if (drawOnlyShadow) {
        int shadowColor = render.getIntPropertyValue(render.ALL.R_SHADOW_COLOR);
        int shadowRadius = render.getIntPropertyValue(render.ALL.R_SHADOW_RADIUS);
        if (shadowColor == 0) {
          shadowColor = rc.shadowRenderingColor;
        }
        drawPolylineShadow(canvas, rc, path, shadowColor, shadowRadius);
      } else {
        boolean update = false;
        if (updatePaint(render, paint, -2, false, rc)) {
          update = true;
          canvas.drawPath(path, paint);
        }
        if (updatePaint(render, paint, -1, false, rc)) {
          update = true;
          canvas.drawPath(path, paint);
        }
        if (update) {
          updatePaint(render, paint, 0, false, rc);
        }
        canvas.drawPath(path, paint);
        if (updatePaint(render, paint, 1, false, rc)) {
          canvas.drawPath(path, paint);
        }
        if (updatePaint(render, paint, 2, false, rc)) {
          canvas.drawPath(path, paint);
        }
      }

      if (oneway != 0 && !drawOnlyShadow) {
        Paint[] paints = oneway == -1 ? getReverseOneWayPaints() : getOneWayPaints();
        for (int i = 0; i < paints.length; i++) {
          canvas.drawPath(path, paints[i]);
        }
      }
      if (textPoints != null) {
        textRenderer.renderText(obj, render, rc, pair, xMid, yMid, path, textPoints);
      }
    }
  }
  public void generateNewBitmap(
      RenderingContext rc,
      List<BinaryMapDataObject> objects,
      Bitmap bmp,
      RenderingRuleSearchRequest render,
      final List<IMapDownloaderCallback> notifyList) {
    long now = System.currentTimeMillis();
    // fill area
    Canvas cv = new Canvas(bmp);
    if (rc.defaultColor != 0) {
      cv.drawColor(rc.defaultColor);
    }
    if (objects != null && !objects.isEmpty() && rc.width > 0 && rc.height > 0) {
      // init rendering context
      rc.tileDivisor = (int) (1 << (31 - rc.zoom));
      rc.cosRotateTileSize = FloatMath.cos((float) Math.toRadians(rc.rotate)) * TILE_SIZE;
      rc.sinRotateTileSize = FloatMath.sin((float) Math.toRadians(rc.rotate)) * TILE_SIZE;

      // put in order map
      TIntObjectHashMap<TIntArrayList> orderMap = sortObjectsByProperOrder(rc, objects, render);

      int objCount = 0;

      int[] keys = orderMap.keys();
      Arrays.sort(keys);

      boolean shadowDrawn = false;

      for (int k = 0; k < keys.length; k++) {
        if (!shadowDrawn
            && (keys[k] >> 2) >= rc.shadowLevelMin
            && (keys[k] >> 2) <= rc.shadowLevelMax
            && rc.shadowRenderingMode > 1) {
          for (int ki = k; ki < keys.length; ki++) {
            if ((keys[ki] >> 2) > rc.shadowLevelMax || rc.interrupted) {
              break;
            }
            TIntArrayList list = orderMap.get(keys[ki]);
            for (int j = 0; j < list.size(); j++) {
              int i = list.get(j);
              int ind = i >> 8;
              int l = i & 0xff;
              BinaryMapDataObject obj = objects.get(ind);

              // show text only for main type
              drawObj(obj, render, cv, rc, l, l == 0, true, (keys[ki] & 3));
              objCount++;
            }
          }
          shadowDrawn = true;
        }
        if (rc.interrupted) {
          return;
        }

        TIntArrayList list = orderMap.get(keys[k]);
        for (int j = 0; j < list.size(); j++) {
          int i = list.get(j);
          int ind = i >> 8;
          int l = i & 0xff;
          BinaryMapDataObject obj = objects.get(ind);

          // show text only for main type
          drawObj(obj, render, cv, rc, l, l == 0, false, (keys[k] & 3));
          objCount++;
        }
        rc.lastRenderedKey = (keys[k] >> 2);
        if (objCount > 25) {
          notifyListeners(notifyList);
          objCount = 0;
        }
      }

      long beforeIconTextTime = System.currentTimeMillis() - now;
      notifyListeners(notifyList);
      drawIconsOverCanvas(rc, cv);

      notifyListeners(notifyList);
      textRenderer.drawTextOverCanvas(rc, cv, rc.useEnglishNames);

      long time = System.currentTimeMillis() - now;
      rc.renderingDebugInfo =
          String.format(
              "Rendering: %s ms  (%s text)\n"
                  + "(%s points, %s points inside, %s of %s objects visible)", //$NON-NLS-1$
              time,
              time - beforeIconTextTime,
              rc.pointCount,
              rc.pointInsideCount,
              rc.visible,
              rc.allObjects);
      log.info(rc.renderingDebugInfo);
    }
  }