LuciadLightspeed allows you to print out the maps displayed in a LuciadLightspeed view, by creating printable objects. In addition, you can display a preview of the layout of your print before you start the printout.
Note that this article focuses on AWT and Swing views. For JavaFX applications, please refer to Printing a JavaFX view.
Creating and configuring a Printable
LuciadLightspeed views can be printed using the java.awt.print
API. The class
ALcdViewComponentPrintable
is an implementation of java.awt.print.Printable
which prints the contents of a given view.
ALcdViewComponentPrintable
defines the following printing properties:
-
DPI: the desired resolution of the print in dots per inch.
-
Feature scale: the scaling of features in the print relative to their size on the screen. A value of 1 means that features such as icons, labels or line widths are printed at the same proportional size as they have on the screen. Values smaller than 1 mean that features become proportionally smaller. For instance, lines appear thinner, and labels become smaller than they are on screen. If label decluttering is used, this also implies that the print potentially displays more labels than the screen. You can also proportionally increase the size of the features by choosing a value between 1 and 2. Feature scale may also affect level-of-detail decisions, for example for raster layers. Lower feature scales may cause higher detail levels to be shown, and the other way round.
-
Horizontal and vertical page count: the number of pages across which the print should be spread out, for multi-page prints.
-
Map scale: the actual map scale that the print will have. Take, for example, a map scale of 1/10000, or 1:10000. This means that 1 meter in world coordinates corresponds to 1/10000m = 0.1mm on paper. The map scale can be derived from the number of pages and the print DPI, or you can set it explicitly. If you do, the page count is derived from the scale, instead of the other way round.
Two implementations of ALcdViewComponentPrintable
are available:
-
TLcdGXYViewComponentPrintable
works with anILcdGXYView
. -
TLspViewComponentPrintable
works with anILspView
.
These classes allow you to set all the printing properties, as well as some additional settings such as the inclusion of crop marks on multi-page prints. Please see the reference documentation for more details.
Examples of how these classes are used can be found in samples.gxy.printing
and
samples.lightspeed.printing
, respectively. These samples also demonstrate how the
printables can be used in conjunction with TLcdPrintPreview
to display a multi-page
print preview dialog. Finally, the samples also illustrate how to add decorations, such as
headers and footers or a legend, to the page surrounding the map.
Supporting printing for custom layers and painters in an ILspView
All the standard layer types available in Lightspeed views support printing out of the box. When
you are implementing your own ILspPaintableLayer
or ILspPainter
, however, it may be
necessary to take some additional steps to make your implementation printing-capable. This section
explains these extra steps.
Tiled rendering
Lightspeed views use a tiled rendering approach to generate the high-resolution images needed for a print. This is normally transparent to the layers and painters in the view, as the tiling is performed through the manipulation of the OpenGL projection matrix. If your custom painting code modifies the projection matrix, however, it may nullify the transformations needed for the tiled rendering.
In such cases, the painter can obtain the bounds of the current tile via
ALspViewXYZWorldTransformation.getClip()
. This method returns a rectangle in view
coordinates which bounds the area of the view that is currently being printed. The painter must
apply the necessary scaling and translation to make this portion of the view fill the entire
viewport.
A typical use case is drawing all manner of overlays on top of the view in screen coordinates. The following code fragment shows how you could set up an orthographic projection matrix for your layer to achieve this. If you do so, however, the layer will no longer print correctly. Therefore, you need to perform additional transformations if the printing clip is not null.
int width = aView.getWidth();
int height = aView.getHeight();
double rasterizationScale = 1.0;
// Switch to an orthographic projection. This breaks printing.
gl.glMatrixMode( ILcdGL.GL_PROJECTION );
gl.glPushMatrix();
gl.glLoadIdentity();
gl.glOrtho( 0, width, height, 0, -1, 1 );
// Apply the print clip if it is set to make the layer print correctly.
ALspViewXYZWorldTransformation w2v = aView.getViewXYZWorldTransformation();
Rectangle2D clip = w2v.getClip();
if ( clip != null ) {
// Scale so the clip fills the view
rasterizationScale = aView.getWidth() / clip.getWidth();
gl.glScaled( rasterizationScale, rasterizationScale, 1.0 );
// Translate so the clip is aligned to the view
gl.glTranslated( -clip.getX(), -clip.getY(), 0 );
}
gl.glMatrixMode( ILcdGL.GL_MODELVIEW );
gl.glPushMatrix();
gl.glLoadIdentity();
// From this point on, draw things in view coordinates.
Feature scale
Custom painters may also want to take the feature scale property, as discussed in Creating and configuring a Printable
, into account. The
current feature scale is available via ALspViewXYZWorldTransformation.getFeatureScale()
.
The feature scale can be applied to line widths, font sizes, texture resolutions, level-of-detail
decisions, and so on, as you see fit. It is important to note, however, that the feature
scale is relative to the print resolution. If the print resolution is 10 times that of the view, and
the feature scale is 0.5, a painter should apply a combined scale factor of 5 to get the expected
result.
The print resolution can be derived from the clip mentioned in the previous section: divide the width or height of the clip by that of the view to obtain the print rasterization scale factor.
The following code, which is continued from the example in Tiled rendering, shows how a line thickness is scaled for printing:
int lineWidth = 2;
gl.glLineWidth( ( float ) (lineWidth * rasterizationScale * w2v.getFeatureScale()) );