protected void renderTiles(
      TileRenderer formater,
      Transformer transformer,
      URI commonUri,
      ParallelMapTileLoader parallelMapTileLoader)
      throws IOException, URISyntaxException {
    final List<URI> urls = new ArrayList<URI>(1);
    final double offsetX;
    final double offsetY;
    final long bitmapTileW;
    final long bitmapTileH;
    int nbTilesW = 0;

    double minGeoX = transformer.getRotatedMinGeoX();
    double minGeoY = transformer.getRotatedMinGeoY();
    double maxGeoX = transformer.getRotatedMaxGeoX();
    double maxGeoY = transformer.getRotatedMaxGeoY();

    if (tileCacheLayerInfo != null) {
      // tiled
      transformer = fixTiledTransformer(transformer);

      if (transformer == null) {
        if (LOGGER.isDebugEnabled()) {
          LOGGER.debug("Resolution out of bounds.");
        }
        urls.add(null);
      }

      bitmapTileW = tileCacheLayerInfo.getWidth();
      bitmapTileH = tileCacheLayerInfo.getHeight();
      final double tileGeoWidth = transformer.getResolution() * bitmapTileW;
      final double tileGeoHeight = transformer.getResolution() * bitmapTileH;

      // TODO I would like to do this sort of thing by extension points for plugins

      // the tileMinGeoSize is not calculated the same way in TileCache
      // and KaMap, so they are treated differently here.
      final double tileMinGeoX;
      final double tileMinGeoY;
      if (this instanceof TileCacheMapReader) {
        if (LOGGER.isDebugEnabled()) {
          LOGGER.debug("TileCacheMapReader min geo x and y calculation used");
        }
        tileMinGeoX =
            (float)
                    (Math.floor((minGeoX - tileCacheLayerInfo.getMinX()) / tileGeoWidth)
                        * tileGeoWidth)
                + tileCacheLayerInfo.getMinX();
        tileMinGeoY =
            (float)
                    (Math.floor((minGeoY - tileCacheLayerInfo.getMinY()) / tileGeoHeight)
                        * tileGeoHeight)
                + tileCacheLayerInfo.getMinY();
      } else if (this instanceof KaMapCacheMapReader || this instanceof KaMapMapReader) {
        if (LOGGER.isDebugEnabled()) {
          LOGGER.debug("Kamap min geo x and y calculation used");
        }
        tileMinGeoX = (float) (Math.floor((minGeoX) / tileGeoWidth) * tileGeoWidth);
        tileMinGeoY = (float) (Math.floor((minGeoY) / tileGeoHeight) * tileGeoHeight);
      } else if (this instanceof WMTSMapReader) {
        if (LOGGER.isDebugEnabled()) {
          LOGGER.debug("WMTS min geo x and y calculation used");
        }

        tileMinGeoX =
            (Math.floor((minGeoX - tileCacheLayerInfo.getMinX()) / tileGeoWidth) * tileGeoWidth)
                + tileCacheLayerInfo.getMinX();
        tileMinGeoY =
            tileCacheLayerInfo.getMaxY()
                - ((Math.ceil((tileCacheLayerInfo.getMaxY() - minGeoY) / tileGeoHeight))
                    * tileGeoHeight);
      } else {
        if (LOGGER.isDebugEnabled()) {
          LOGGER.debug("Default min geo x and y calculation used");
        }
        tileMinGeoX =
            (float)
                    (Math.floor((minGeoX - tileCacheLayerInfo.getMinX()) / tileGeoWidth)
                        * tileGeoWidth)
                + tileCacheLayerInfo.getMinX();
        tileMinGeoY =
            (float)
                    (Math.floor((minGeoY - tileCacheLayerInfo.getMinY()) / tileGeoHeight)
                        * tileGeoHeight)
                + tileCacheLayerInfo.getMinY();
      }

      // Linksverschiebung des Kachelstartpunktes in Pixel
      offsetX = (minGeoX - tileMinGeoX) / transformer.getResolution();
      // Höhenverschiebung (nach unten) des Kachelstartpunktes in Pixel
      offsetY = (minGeoY - tileMinGeoY) / transformer.getResolution();
      for (double geoY = tileMinGeoY; geoY < maxGeoY; geoY += tileGeoHeight) {
        nbTilesW = 0;
        for (double geoX = tileMinGeoX; geoX < maxGeoX; geoX += tileGeoWidth) {
          nbTilesW++;

          if (tileCacheLayerInfo.isVisible(geoX, geoY, geoX + tileGeoWidth, geoY + tileGeoHeight)) {
            double exactLeftX = 958760.7160000019;
            double exactBottomY = 6389320.947999999;
            boolean positionCorrectX = geoX <= (exactLeftX + 0.25) && geoX >= (exactLeftX - 0.25);
            boolean positionCorrectY =
                geoY <= (exactBottomY + 0.25) && geoY >= (exactBottomY - 0.25);
            boolean positionCorrect = positionCorrectX && positionCorrectY;
            LOGGER.debug(positionCorrect ? "Position Correct" : "Position Incorrect");
            urls.add(
                getTileUri(
                    commonUri,
                    transformer,
                    geoX,
                    geoY,
                    geoX + tileGeoWidth,
                    geoY + tileGeoHeight,
                    bitmapTileW,
                    bitmapTileH));
          } else {
            if (LOGGER.isDebugEnabled()) {
              LOGGER.debug(
                  "Tile out of bounds: "
                      + getTileUri(
                          commonUri,
                          transformer,
                          geoX,
                          geoY,
                          geoX + tileGeoWidth,
                          geoY + tileGeoHeight,
                          bitmapTileW,
                          bitmapTileH));
            }
            urls.add(null);
          }
        }
      }

    } else {
      // single tile
      nbTilesW = 1;
      offsetX = 0;
      offsetY = 0;
      bitmapTileW = transformer.getRotatedBitmapW();
      bitmapTileH = transformer.getRotatedBitmapH();
      urls.add(
          getTileUri(
              commonUri,
              transformer,
              minGeoX,
              minGeoY,
              maxGeoX,
              maxGeoY,
              bitmapTileW,
              bitmapTileH));
    }

    // TODO Hier wird falsch scaliert
    formater.render(
        transformer,
        urls,
        parallelMapTileLoader,
        context,
        opacity,
        nbTilesW,
        offsetX,
        offsetY,
        bitmapTileW,
        bitmapTileH);
  }