public void addZoomLevel(ReferencedEnvelope bbox, int tileWidth, int tileHeight) {
   List<Grid> list = grids.getModelObject();
   final Grid newGrid = new Grid();
   if (list.isEmpty()) {
     BoundingBox extent =
         new BoundingBox(bbox.getMinX(), bbox.getMinY(), bbox.getMaxX(), bbox.getMaxY());
     final int levels = 1;
     GridSet tmpGridset =
         GridSetFactory.createGridSet(
             "stub",
             SRS.getEPSG4326(),
             extent,
             false,
             levels,
             1D,
             GridSetFactory.DEFAULT_PIXEL_SIZE_METER,
             tileWidth,
             tileHeight,
             false);
     Grid grid = tmpGridset.getGridLevels()[0];
     newGrid.setResolution(grid.getResolution());
     newGrid.setScaleDenominator(grid.getScaleDenominator());
   } else {
     Grid prev = list.get(list.size() - 1);
     newGrid.setResolution(prev.getResolution() / 2);
     newGrid.setScaleDenominator(prev.getScaleDenominator() / 2);
   }
   list.add(newGrid);
   grids.setModelObject(list);
   // TileMatrixSetEditor.this.convertInput();
 }
  @Test
  public void testCascadeGetLegendGraphics() throws Exception {
    // setup the layer
    WMSLayer layer = createWMSLayer("image/png");
    final byte[] responseBody = new String("Fake body").getBytes();
    layer.setSourceHelper(
        new WMSHttpHelper() {
          @Override
          public GetMethod executeRequest(
              URL url, Map<String, String> queryParams, Integer backendTimeout)
              throws HttpException, IOException {
            GetMethod response = EasyMock.createMock(GetMethod.class);
            expect(response.getStatusCode()).andReturn(200);
            expect(response.getResponseBodyAsStream())
                .andReturn(new ByteArrayInputStream(responseBody));
            expect(response.getResponseCharSet()).andReturn("UTF-8");
            expect(response.getResponseHeader("Content-Type"))
                .andReturn(new Header("Content-Type", "image/png"));
            response.releaseConnection();
            expectLastCall();
            replay(response);
            return response;
          }
        });
    MockLockProvider lockProvider = new MockLockProvider();
    layer.setLockProvider(lockProvider);

    // setup the conveyor tile
    final StorageBroker mockStorageBroker = EasyMock.createMock(StorageBroker.class);

    String layerId = layer.getName();
    MockHttpServletRequest servletReq = new MockHttpServletRequest();
    servletReq.setQueryString(
        "REQUEST=GetLegendGraphic&VERSION=1.0.0&FORMAT=image/png&WIDTH=20&HEIGHT=20&LAYER=topp:states");
    MockHttpServletResponse servletResp = new MockHttpServletResponse();

    long[] gridLoc = {0, 0, 0}; // x, y, level
    MimeType mimeType = layer.getMimeTypes().get(0);
    GridSet gridSet = gridSetBroker.WORLD_EPSG4326;
    String gridSetId = gridSet.getName();
    ConveyorTile tile =
        new ConveyorTile(
            mockStorageBroker,
            layerId,
            gridSetId,
            gridLoc,
            mimeType,
            null,
            servletReq,
            servletResp);

    // proxy the request, and check the response
    layer.proxyRequest(tile);

    assertEquals(200, servletResp.getStatus());
    assertEquals("Fake body", servletResp.getContentAsString());
    assertEquals("image/png", servletResp.getContentType());
  }
Beispiel #3
0
    @Override
    public void validate(IValidatable<Set<XMLGridSubset>> validatable) {
      if (!validate) {
        return;
      }
      Set<XMLGridSubset> gridSubsets = validatable.getValue();
      if (gridSubsets == null || gridSubsets.size() == 0) {
        error(validatable, "GridSubsetsEditor.validation.empty");
        return;
      }

      final GWC gwc = GWC.get();
      for (XMLGridSubset subset : gridSubsets) {
        final String gridSetName = subset.getGridSetName();
        final Integer zoomStart = subset.getZoomStart();
        final Integer zoomStop = subset.getZoomStop();
        final BoundingBox extent = subset.getExtent();

        if (gridSetName == null) {
          throw new IllegalStateException("GridSet name is null");
        }

        if (zoomStart != null && zoomStop != null) {
          if (zoomStart.intValue() > zoomStop.intValue()) {
            error(validatable, "GridSubsetsEditor.validation.zoomLevelsError");
            return;
          }
        }

        final GridSetBroker gridSetBroker = gwc.getGridSetBroker();
        final GridSet gridSet = gridSetBroker.get(gridSetName);

        if (null == gridSet) {
          error(validatable, "GridSubsetsEditor.validation.gridSetNotFound", gridSetName);
          return;
        }

        if (extent != null) {
          if (extent.isNull() || !extent.isSane()) {
            error(validatable, "GridSubsetsEditor.validation.invalidBounds");
          }
          final BoundingBox fullBounds = gridSet.getOriginalExtent();
          final boolean intersects = fullBounds.intersects(extent);
          if (!intersects) {
            error(validatable, "GridSubsetsEditor.validation.boundsOutsideCoverage");
          }
        }
      }
    }
  @Test
  public void testSeedMetaTiled() throws Exception {
    WMSLayer layer = createWMSLayer("image/png");

    WMSSourceHelper mockSourceHelper = new MockWMSSourceHelper();
    MockLockProvider lockProvider = new MockLockProvider();
    layer.setSourceHelper(mockSourceHelper);
    layer.setLockProvider(lockProvider);

    final StorageBroker mockStorageBroker = EasyMock.createMock(StorageBroker.class);
    Capture<TileObject> captured = new Capture<TileObject>();
    expect(mockStorageBroker.put(EasyMock.capture(captured))).andReturn(true).anyTimes();
    replay(mockStorageBroker);

    String layerId = layer.getName();
    HttpServletRequest servletReq = new MockHttpServletRequest();
    HttpServletResponse servletResp = new MockHttpServletResponse();

    long[] gridLoc = {0, 0, 0}; // x, y, level
    MimeType mimeType = layer.getMimeTypes().get(0);
    GridSet gridSet = gridSetBroker.WORLD_EPSG4326;
    String gridSetId = gridSet.getName();
    ConveyorTile tile =
        new ConveyorTile(
            mockStorageBroker,
            layerId,
            gridSetId,
            gridLoc,
            mimeType,
            null,
            servletReq,
            servletResp);

    boolean tryCache = false;
    layer.seedTile(tile, tryCache);

    assertEquals(1, captured.getValues().size());
    TileObject value = captured.getValue();
    assertNotNull(value);
    assertEquals("image/png", value.getBlobFormat());
    assertNotNull(value.getBlob());
    assertTrue(value.getBlob().getSize() > 0);

    verify(mockStorageBroker);

    // check the lock provider was called in a symmetric way
    lockProvider.verify();
    lockProvider.clear();
  }
Beispiel #5
0
  private GeoServerTileLayer mockTileLayer(String layerName, List<String> gridSetNames)
      throws Exception {

    GeoServerTileLayer tileLayer = mock(GeoServerTileLayer.class);
    when(tld.layerExists(eq(layerName))).thenReturn(true);
    when(tld.getTileLayer(eq(layerName))).thenReturn(tileLayer);
    when(tileLayer.getName()).thenReturn(layerName);
    when(tileLayer.isEnabled()).thenReturn(true);

    final MimeType mimeType1 = MimeType.createFromFormat("image/png");
    final MimeType mimeType2 = MimeType.createFromFormat("image/jpeg");
    when(tileLayer.getMimeTypes()).thenReturn(ImmutableList.of(mimeType1, mimeType2));

    Map<String, GridSubset> subsets = Maps.newHashMap();
    Multimap<SRS, GridSubset> bySrs = LinkedHashMultimap.create();

    GridSetBroker broker = gridSetBroker;

    for (String gsetName : gridSetNames) {
      GridSet gridSet = broker.get(gsetName);
      XMLGridSubset xmlGridSubset = new XMLGridSubset();
      String gridSetName = gridSet.getName();
      xmlGridSubset.setGridSetName(gridSetName);
      GridSubset gridSubSet = xmlGridSubset.getGridSubSet(broker);
      subsets.put(gsetName, gridSubSet);
      bySrs.put(gridSet.getSrs(), gridSubSet);

      when(tileLayer.getGridSubset(eq(gsetName))).thenReturn(gridSubSet);
    }
    for (SRS srs : bySrs.keySet()) {
      List<GridSubset> list = ImmutableList.copyOf(bySrs.get(srs));
      when(tileLayer.getGridSubsetsForSRS(eq(srs))).thenReturn(list);
    }
    when(tileLayer.getGridSubsets()).thenReturn(subsets.keySet());

    // sanity check
    for (String gsetName : gridSetNames) {
      assertTrue(tileLayer.getGridSubsets().contains(gsetName));
      assertNotNull(tileLayer.getGridSubset(gsetName));
    }

    return tileLayer;
  }
  Integer findClosestZoom(GridSet gridSet, WMSMapContent map) {
    double reqScale =
        RendererUtilities.calculateOGCScale(bounds(map), gridSet.getTileWidth(), null);

    int i = 0;
    double error = Math.abs(gridSet.getGrid(i).getScaleDenominator() - reqScale);
    while (i < gridSet.getNumLevels() - 1) {
      Grid g = gridSet.getGrid(i + 1);
      double e = Math.abs(g.getScaleDenominator() - reqScale);

      if (e > error) {
        break;
      } else {
        error = e;
      }
      i++;
    }

    return Math.max(i, 0);
  }
  int[] findMinMaxZoom(GridSubset gridSubset, WMSMapContent map) {
    GridSet gridSet = gridSubset.getGridSet();
    Map formatOpts = map.getRequest().getFormatOptions();

    Integer minZoom = null;
    if (formatOpts.containsKey("min_zoom")) {
      minZoom = Integer.parseInt(formatOpts.get("min_zoom").toString());
    }
    if (minZoom == null) {
      minZoom = findClosestZoom(gridSet, map);
    }

    Integer maxZoom = null;
    if (formatOpts.containsKey("max_zoom")) {
      maxZoom = Integer.parseInt(formatOpts.get("max_zoom").toString());
    } else if (formatOpts.containsKey("num_zooms")) {
      maxZoom = minZoom + Integer.parseInt(formatOpts.get("num_zooms").toString());
    }

    if (maxZoom == null) {
      // walk down until we hit too many tiles
      maxZoom = findMaxZoomAuto(gridSubset, minZoom, map);
    }

    if (maxZoom < minZoom) {
      throw new ServiceException(
          format("maxZoom (%d) can not be less than minZoom (%d)", maxZoom, minZoom));
    }

    // end index
    if (maxZoom > gridSet.getNumLevels()) {
      LOGGER.warning(
          format(
              "Max zoom (%d) can't be greater than number of zoom levels (%d)",
              maxZoom, gridSet.getNumLevels()));
      maxZoom = gridSet.getNumLevels();
    }

    return new int[] {minZoom, maxZoom};
  }
 private void tileMatrixSet(StringBuilder str, GridSet gridSet) {
   str.append("  <TileMatrixSet>\n");
   str.append("    <ows:Identifier>" + gridSet.getName() + "</ows:Identifier>\n");
   // If the following is not good enough, please get in touch and we will try to fix it :)
   str.append(
       "    <ows:SupportedCRS>urn:ogc:def:crs:EPSG::"
           + gridSet.getSrs().getNumber()
           + "</ows:SupportedCRS>\n");
   // TODO detect these str.append("
   // <WellKnownScaleSet>urn:ogc:def:wkss:GlobalCRS84Pixel</WellKnownScaleSet>\n");
   Grid[] grids = gridSet.getGrids();
   for (int i = 0; i < grids.length; i++) {
     double[] tlCoordinates = gridSet.getOrderedTopLeftCorner(i);
     tileMatrix(
         str,
         grids[i],
         tlCoordinates,
         gridSet.getTileWidth(),
         gridSet.getTileHeight(),
         gridSet.isScaleWarning());
   }
   str.append("  </TileMatrixSet>\n");
 }
  @Test
  public void testSeedJpegPngMetaTiled() throws Exception {
    WMSLayer layer = createWMSLayer("image/vnd.jpeg-png");

    WMSSourceHelper mockSourceHelper =
        new WMSSourceHelper() {

          @Override
          protected void makeRequest(
              TileResponseReceiver tileRespRecv,
              WMSLayer layer,
              Map<String, String> wmsParams,
              MimeType expectedMimeType,
              Resource target)
              throws GeoWebCacheException {
            int width = Integer.parseInt(wmsParams.get("WIDTH"));
            int height = Integer.parseInt(wmsParams.get("HEIGHT"));
            assertEquals(768, width);
            assertEquals(768, height);
            BufferedImage img = new BufferedImage(width, height, BufferedImage.TYPE_4BYTE_ABGR);
            Graphics2D graphics = img.createGraphics();
            graphics.setColor(Color.BLACK);
            // fill an L shaped set of tiles, making a few partially filled
            graphics.fillRect(0, 0, width, 300);
            graphics.fillRect(0, 0, 300, height);
            graphics.dispose();
            ByteArrayOutputStream output = new ByteArrayOutputStream();
            try {
              ImageIO.write(img, "PNG", output);
              ImageIO.write(img, "PNG", new java.io.File("/tmp/meta.png"));
            } catch (IOException e) {
              throw new RuntimeException(e);
            }

            try {
              target.transferFrom(
                  Channels.newChannel(new ByteArrayInputStream(output.toByteArray())));
            } catch (IOException e) {
              throw new RuntimeException(e);
            }
          }
        };
    MockLockProvider lockProvider = new MockLockProvider();
    layer.setSourceHelper(mockSourceHelper);
    layer.setLockProvider(lockProvider);

    final StorageBroker mockStorageBroker = EasyMock.createMock(StorageBroker.class);
    Capture<TileObject> captured = new Capture<TileObject>(CaptureType.ALL);
    expect(mockStorageBroker.put(EasyMock.capture(captured)))
        .andAnswer(
            new IAnswer<Boolean>() {

              @Override
              public Boolean answer() throws Throwable {
                TileObject to = (TileObject) EasyMock.getCurrentArguments()[0];
                assertEquals("image/vnd.jpeg-png", to.getBlobFormat());
                assertNotNull(to.getBlob());
                assertTrue(to.getBlob().getSize() > 0);
                String format = ImageMime.jpegPng.getMimeType(to.getBlob());
                long[] xyz = to.getXYZ();
                assertEquals(10, xyz[2]);
                // check the ones in the full black area are jpeg, the others png
                if (xyz[0] == 900 || xyz[1] == 602) {
                  assertEquals("image/jpeg", format);
                } else {
                  assertEquals("image/png", format);
                }

                return true;
              }
            })
        .anyTimes();
    replay(mockStorageBroker);

    String layerId = layer.getName();
    HttpServletRequest servletReq = new MockHttpServletRequest();
    HttpServletResponse servletResp = new MockHttpServletResponse();

    long[] gridLoc = {900, 600, 10}; // x, y, level
    MimeType mimeType = layer.getMimeTypes().get(0);
    GridSet gridSet = gridSetBroker.WORLD_EPSG4326;
    String gridSetId = gridSet.getName();
    ConveyorTile tile =
        new ConveyorTile(
            mockStorageBroker,
            layerId,
            gridSetId,
            gridLoc,
            mimeType,
            null,
            servletReq,
            servletResp);

    boolean tryCache = false;
    layer.seedTile(tile, tryCache);

    assertEquals(9, captured.getValues().size());
    verify(mockStorageBroker);

    // check the lock provider was called in a symmetric way
    lockProvider.verify();
    lockProvider.clear();
  }
Beispiel #10
0
  private static String generateHTML(
      TileLayer layer, String gridSetStr, String formatStr, boolean asPlugin)
      throws GeoWebCacheException {
    String layerName = layer.getName();

    GridSubset gridSubset = layer.getGridSubset(gridSetStr);

    BoundingBox bbox = gridSubset.getGridSetBounds();
    BoundingBox zoomBounds = gridSubset.getOriginalExtent();

    String res = "resolutions: " + Arrays.toString(gridSubset.getResolutions()) + ",\n";

    String units = "units: \"" + gridSubset.getGridSet().guessMapUnits() + "\",\n";

    String openLayersPath;
    if (asPlugin) {
      openLayersPath = "../openlayers/OpenLayers.js";
    } else {
      openLayersPath = "../openlayers/OpenLayers.js";
    }

    String page =
        "<html xmlns=\"http://www.w3.org/1999/xhtml\"><head>\n"
            + "<meta http-equiv=\"imagetoolbar\" content=\"no\">\n"
            + "<title>"
            + layerName
            + " "
            + gridSubset.getName()
            + " "
            + formatStr
            + "</title>\n"
            + "<style type=\"text/css\">\n"
            + "body { font-family: sans-serif; font-weight: bold; font-size: .8em; }\n"
            + "body { border: 0px; margin: 0px; padding: 0px; }\n"
            + "#map { width: 85%; height: 85%; border: 0px; padding: 0px; }\n"
            + "</style>\n"
            + "<script src=\""
            + openLayersPath
            + "\"></script>    \n"
            + "<script type=\"text/javascript\">               \n"
            + "var map, demolayer;                               \n"
            + "  // sets the chosen modifiable parameter        \n"
            + "  function setParam(name, value){                \n"
            + "   str = \"demolayer.mergeNewParams({\" + name + \": '\" + value + \"'})\" \n"
            + "   // alert(str);                                   \n"
            + "   eval(str);                                    \n"
            + "  }                                              \n"
            + "OpenLayers.DOTS_PER_INCH = "
            + gridSubset.getDotsPerInch()
            + ";\n"
            + "OpenLayers.Util.onImageLoadErrorColor = 'transparent';\n"
            + "function init(){\n"
            + "var mapOptions = { \n"
            + res
            + "projection: new OpenLayers.Projection('"
            + gridSubset.getSRS().toString()
            + "'),\n"
            + "maxExtent: new OpenLayers.Bounds("
            + bbox.toString()
            + "),\n"
            + units
            + "controls: []\n"
            + "};\n"
            + "map = new OpenLayers.Map('map', mapOptions );\n"
            + "map.addControl(new OpenLayers.Control.PanZoomBar({\n"
            + "		position: new OpenLayers.Pixel(2, 15)\n"
            + "}));\n"
            + "map.addControl(new OpenLayers.Control.Navigation());\n"
            + "map.addControl(new OpenLayers.Control.Scale($('scale')));\n"
            + "map.addControl(new OpenLayers.Control.MousePosition({element: $('location')}));\n"
            + "demolayer = new OpenLayers.Layer.WMS(\n"
            + "\""
            + layerName
            + "\",\"../service/wms\",\n"
            + "{layers: '"
            + layerName
            + "', format: '"
            + formatStr
            + "' },\n"
            + "{ tileSize: new OpenLayers.Size("
            + gridSubset.getTileWidth()
            + ","
            + gridSubset.getTileHeight()
            + ")";

    /*
     * If the gridset has a top left tile origin, lets tell that to open layers. Otherwise it'll
     * calculate tile bounds based on the bbox bottom left corner, leading to misaligned
     * requests.
     */
    GridSet gridSet = gridSubset.getGridSet();
    if (gridSet.isTopLeftAligned()) {
      page +=
          ",\n tileOrigin: new OpenLayers.LonLat(" + bbox.getMinX() + ", " + bbox.getMaxY() + ")";
    }

    page +=
        "});\n"
            + "map.addLayer(demolayer);\n"
            + "map.zoomToExtent(new OpenLayers.Bounds("
            + zoomBounds.toString()
            + "));\n"
            + "// The following is just for GetFeatureInfo, which is not cached. Most people do not need this \n"
            + "map.events.register('click', map, function (e) {\n"
            + "  document.getElementById('nodelist').innerHTML = \"Loading... please wait...\";\n"
            + "  var params = {\n"
            + "    REQUEST: \"GetFeatureInfo\",\n"
            + "    EXCEPTIONS: \"application/vnd.ogc.se_xml\",\n"
            + "    BBOX: map.getExtent().toBBOX(),\n"
            + "    X: e.xy.x,\n"
            + "    Y: e.xy.y,\n"
            + "    INFO_FORMAT: 'text/html',\n"
            + "    QUERY_LAYERS: map.layers[0].params.LAYERS,\n"
            + "    FEATURE_COUNT: 50,\n"
            + "    Layers: '"
            + layerName
            + "',\n"
            + "    Styles: '',\n"
            + "    Srs: '"
            + gridSubset.getSRS().toString()
            + "',\n"
            + "    WIDTH: map.size.w,\n"
            + "    HEIGHT: map.size.h,\n"
            + "    format: \""
            + formatStr
            + "\" };\n"
            + "  OpenLayers.loadURL(\"../service/wms\", params, this, setHTML, setHTML);\n"
            + "  OpenLayers.Event.stop(e);\n"
            + "  });\n"
            + "}\n"
            + "function setHTML(response){\n"
            + "    document.getElementById('nodelist').innerHTML = response.responseText;\n"
            + "};\n"
            + "</script>\n"
            + "</head>\n"
            + "<body onload=\"init()\">\n"
            + "<div id=\"params\">"
            + makeModifiableParameters(layer)
            + "</div>\n"
            + "<div id=\"map\"></div>\n"
            + "<div id=\"nodelist\"></div>\n"
            + "</body>\n"
            + "</html>";
    return page;
  }
  void addTileLayers(GeoPackage geopkg, List<MapLayerInfo> mapLayers, WMSMapContent map)
      throws IOException {

    if (mapLayers.isEmpty()) {
      return;
    }

    // figure out a name for the file entry
    String tileEntryName = null;
    Map formatOpts = map.getRequest().getFormatOptions();
    if (formatOpts.containsKey("tileset_name")) {
      tileEntryName = (String) formatOpts.get("tileset_name");
    }
    if (tileEntryName == null) {
      tileEntryName = map.getTitle();
    }
    if (tileEntryName == null && mapLayers.size() == 1) {
      Iterator<MapLayerInfo> it = mapLayers.iterator();
      tileEntryName = it.next().getLayerInfo().getName();
    }

    GridSubset gridSubset = findBestGridSubset(map);
    int[] minmax = findMinMaxZoom(gridSubset, map);

    BoundingBox bbox = bbox(map);

    TileEntry e = new TileEntry();
    e.setTableName(tileEntryName);

    if (mapLayers.size() == 1) {
      ResourceInfo r = mapLayers.get(0).getResource();
      e.setIdentifier(r.getTitle());
      e.setDescription(r.getAbstract());
    }
    e.setBounds(
        new ReferencedEnvelope(
            findTileBounds(gridSubset, bbox, minmax[0]), map.getCoordinateReferenceSystem()));
    e.setSrid(srid(map));

    GridSet gridSet = gridSubset.getGridSet();
    for (int z = minmax[0]; z < minmax[1]; z++) {
      Grid g = gridSet.getGrid(z);

      TileMatrix m = new TileMatrix();
      m.setZoomLevel(z);
      m.setMatrixWidth((int) g.getNumTilesWide());
      m.setMatrixHeight((int) g.getNumTilesHigh());
      m.setTileWidth(gridSubset.getTileWidth());
      m.setTileHeight(gridSubset.getTileHeight());

      // TODO: not sure about this
      m.setXPixelSize(g.getResolution());
      m.setYPixelSize(g.getResolution());
      // m.setXPixelSize(gridSet.getPixelSize());
      // m.setYPixelSize(gridSet.getPixelSize());

      e.getTileMatricies().add(m);
    }

    // figure out the actual bounds of the tiles to be renderered
    LOGGER.fine("Creating tile entry" + e.getTableName());
    geopkg.create(e);

    // create a prototype getmap request
    GetMapRequest req = new GetMapRequest();
    OwsUtils.copy(map.getRequest(), req, GetMapRequest.class);
    req.setLayers(mapLayers);

    String imageFormat =
        formatOpts.containsKey("format") ? parseFormatFromOpts(formatOpts) : findBestFormat(map);

    req.setFormat(imageFormat);
    req.setWidth(gridSubset.getTileWidth());
    req.setHeight(gridSubset.getTileHeight());

    // count tiles as we generate them
    int ntiles = 0;

    // flag determining if tile row indexes we store in database should be inverted
    boolean flipy = Boolean.valueOf((String) formatOpts.get("flipy"));
    for (int z = minmax[0]; z < minmax[1]; z++) {
      long[] intersect = gridSubset.getCoverageIntersection(z, bbox);
      for (long x = intersect[0]; x <= intersect[2]; x++) {
        for (long y = intersect[1]; y <= intersect[3]; y++) {
          BoundingBox box = gridSubset.boundsFromIndex(new long[] {x, y, z});
          req.setBbox(new Envelope(box.getMinX(), box.getMaxX(), box.getMinY(), box.getMaxY()));

          Tile t = new Tile();
          t.setZoom(z);
          t.setColumn((int) x);
          t.setRow((int) (flipy ? gridSubset.getNumTilesHigh(z) - (y + 1) : y));

          WebMap result = webMapService.getMap(req);
          t.setData(toBytes(result));

          geopkg.add(e, t);

          // images we encode are actually kept around, we need to clean them up
          if (ntiles++ == TILE_CLEANUP_INTERVAL) {
            cleanUpImages();
            ntiles = 0;
          }
        }
      }
    }
  }