GTRenderer to draw maps

GTRenderer renderer is the reason why you signed up for this whole GeoTools experience; you want to see a Map.

GTRenderer is actually an interface; currently there are two implementations:

  • StreamingRenderer - a great implementation that does not cache anything. This decision makes it easier to understand and allows it to tackle that large data sets without running out of memory.

  • ShapefileRenderer - (unsupported) restricted to shapefiles as a playground for trying out speed improvements. However all the good optimizations have been picked up by StreamingRenderer.

../../_images/GTRenderer.PNG

Here is how to drawn an outputArea rectangle:

GTRenderer draw = new StreamingRenderer();
draw.setMapContent(map);

draw.paint(g2d, outputArea, map.getLayerBounds() );

If you have completed the quick start, this is the approach used by the JMapPane class we use in a lot of our tutorials.

The important part of the above example is that GTRenderer works on the Java2D class Graphics2D. You can find many implementations of Graphics2D allowing GeoTools to work with a range of graphics systems beyond the screen.

Swing

You can create a swing control to render the image interactively; we provide an example JMapPane for use in our tutorials.

GTRenderer is just a rendering engine - in your own application you may consider the following ideas:

  • Experiment with different Java 2D graphics settings such as anti-aliasing

  • Use background threads to draw tiles, and allowing your swing control to pull the tiles onto the screen for a smooth “slippy” map

3D

A Google summer of code student put together and example of rendering into a texture buffers and using OpenGL to handle panning and zooming.

Personally I would look for an Graphics2D implementation that was backed by OpenGL commands and use GeoTools to render out into the scene graph.

If you are interested in the students code it is currently in the “spike” directory of GeoTools where we keep experiments.

Printing

The Java2D library is also used for Java printing. You can print using StreamingRenderer, the code works like normal just use the Graphics2D object from your Printer.

uDig uses this facility to allow for printing maps directly to the printer.

PDF

We have also had success using GTRenderer and Batik for the generation of PDF output (they provide a Graphics2D object).

You can see this functionality in uDig and GeoServer.

The following example is taken from uDig:

  Rectangle suggestedPageSize = getITextPageSize(page1.getPageSize());
  Rectangle pageSize = rotatePageIfNecessary(suggestedPageSize);
  //rotate if we need landscape
  Document document = new Document(pageSize);
  ...
  PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream(outputFile));
  document.open();
  Graphics2D graphics = cb.createGraphics(pageSize.getWidth(), pageSize.getHeight());

  // call your GTRenderer here
  GTRenderer draw = new StreamingRenderer();
  draw.setMapContent(map);

  draw.paint(graphics, outputArea, map.getLayerBounds() );

  // cleanup
  graphics.dispose();

  //cleanup
  document.close();
  writer.close();

PDF Tips:

You may wish to increase the page size by 2 and then scale the result by 50% in order to produce high resolution raster layers

SVG

The GeoVista team have used the Batik project to generate SVG output.

You will need to manage the Batik dependencies yourself, make sure to include batik-codec if your map contains any image based graphics.

Thanks to James Macgill for the following code example:

    /**
     * Generate an SVG document from the supplied information. Note, use cavasSize first if you want
     * to change the default output size.
     *
     * @param map Contains the layers (features + styles) to be rendered
     * @param env The portion of the map to generate an SVG from
     * @param out Stream to write the resulting SVG out to (probable should be a new file)
     * @param canvasSize optional canvas size, will default to 300x300
     * @throws IOException Should anything go wrong whilst writing to 'out'
     * @throws ParserConfigurationException If critical XML tools are missing from the classpath
     */
    public static void exportSVG(
            MapContent map, ReferencedEnvelope env, OutputStream out, Dimension canvasSize)
            throws IOException, ParserConfigurationException {
        if (canvasSize == null) {
            canvasSize = new Dimension(300, 300); // default of 300x300
        }

        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
        DocumentBuilder db = dbf.newDocumentBuilder();

        // Create an instance of org.w3c.dom.Document
        Document document = db.getDOMImplementation().createDocument(null, "svg", null);

        // Set up the map
        SVGGeneratorContext ctx1 = SVGGeneratorContext.createDefault(document);
        SVGGeneratorContext ctx = ctx1;
        ctx.setComment("Generated by GeoTools2 with Batik SVG Generator");

        SVGGraphics2D g2d = new SVGGraphics2D(ctx, true);

        g2d.setSVGCanvasSize(canvasSize);

        StreamingRenderer renderer = new StreamingRenderer();
        renderer.setMapContent(map);

        Rectangle outputArea = new Rectangle(g2d.getSVGCanvasSize());
        ReferencedEnvelope dataArea = map.getMaxBounds();

        LOGGER.finest("rendering map");
        renderer.paint(g2d, outputArea, dataArea);
        LOGGER.finest("writing to file");
        try (OutputStreamWriter osw = new OutputStreamWriter(out, StandardCharsets.UTF_8)) {
            g2d.stream(osw);
        }
    }

Image

You can also ask Java to make you a Graphics2D for a BufferedImage in memory. After drawing into this image you can write it out to disk.

Here is an example from Oliver on the email list (modified slightly to use current GeoTools classes):

public void saveImage(final MapContent map, final String file, final int imageWidth) {

    GTRenderer renderer = new StreamingRenderer();
    renderer.setMapContent(map);

    Rectangle imageBounds = null;
    ReferencedEnvelope mapBounds = null;
    try {
        mapBounds = map.getMaxBounds();
        double heightToWidth = mapBounds.getSpan(1) / mapBounds.getSpan(0);
        imageBounds = new Rectangle(
                0, 0, imageWidth, (int) Math.round(imageWidth * heightToWidth));

    } catch (Exception e) {
        // failed to access map layers
        throw new RuntimeException(e);
    }

    BufferedImage image = new BufferedImage(imageBounds.width, imageBounds.height, BufferedImage.TYPE_INT_RGB);

    Graphics2D gr = image.createGraphics();
    gr.setPaint(Color.WHITE);
    gr.fill(imageBounds);

    try {
        renderer.paint(gr, imageBounds, mapBounds);
        File fileToSave = new File(file);
        ImageIO.write(image, "jpeg", fileToSave);

    } catch (IOException e) {
        throw new RuntimeException(e);
    }
}