GeoTools : MapLayerArchitecture - 5_Rendering

Work on the rendering framework.

On this page ideas specifically for a new rendering framework are to be found.
For goals see also this page.

General goals for the rendering framework

  • It should be possible to have one renderer for many (if not all) data types, but it should be equally possible to have specialized renderers for certain types of data. My idea is, to have a MapRenderer interface, whose implementation can (but need not) delegate the work to a specialized MapLayerRenderer for every map layer of the map.
  • If different renderers are used they might need to exchange information
  • Renderer architecture should be modular as well (i.e. create reusable Labelers)
  • Renderers need to give progress feedback, so a ProgressListener must be used
  • It must be cared about timing issues. F.e. if rendering is done in a separate thread then the user might, in the meantime resize the window, reorder layers or otherwise change the rendering related environment.
  • The MapRenderer (see above) interface should have methods to rerender only a certain area of the map (where the content changed) or to render only layers above all layers. The MapRenderer implementation can, but does not need to support this notion. However rendering only the needed areas and layers could speed up things (but might require more memory).
  • I don't add "compatibility with the existing GeoTools renderer interfaces" here. I'm not sure if this is possible or makes sense. But I hope that is is possible to utilize the "LiteRenderer" within this framework with just a thin wrapper around it.
  • Rendering to SWT must be possible in principle. It looks as if the "Graphics2DRenderer" solution is the solution of choice here. It converts graphics rendererd on a Graphics2D object to graphics for the SWT "GraphicsContext".
  • Support the distinction between "map scale" and "zoom factor" for map panes. This is discussed in more detail later.

Implementation ideas, UML diagrams and such

Map scale vs. zoom factor

Some months ago there was a discussion about map scale and zoom factor on the geotools list. My opinion was - and is - that it would be a good idea to separate the two. Note that this does not say, that every map pane must support the notion of "zooming".
Basically the difference betwenn the two is:

  • If map scale changes, the map contents may (not must!) change. For example, a 1:100000 map might show only generalized building blocks and major roads, whereas when the map scale is changed to 1:25000 the single buildings and smaller streets appear. The SLD specification explicitely allows this. Also, between different map scales, the ratio between border width and interior of polygons might change, as an example.
  • If the zoom factor changes, the map contents does not change, nor do the proportions of objects or the interior/edge ratios change. The map just gets magnified or scaled down proportionally.

I have a program for topographical maps on my PC that shows this disctintion very good: You can switch between different raster maps for 1:25000, 1:50000, 1:100000, but independent from this you can zoom in and out for each of these maps.

What matters for the renderer?

  • The total zoom (i.e. map scale x zoom factor) is important for the "screen to world" transformation to determine where the geometry lies (where the vertices are).
  • The map scale is relevant for the style to determine which layers/rules to paint
  • The zoom factor is relevant, since line widths and point sizes need to get multiplied with it.
    As said, supporting the notion of zooming is not necessary for a map pane, but the render framework should be prepared to handle map scale and zoom factor correctly.

Pending: How should this work for raster images? When using zoom factor 8 as an example, should one see "raster pixels" of 8x8 pixel size or should raster images ignore this?

How to distribute the work between which classes

Please not that I use "working names" here. These might not become the final class names. Also note that names being equal to uDig classes do not necessary refer to exactly this class.

  1. RenderTarget - base class for object on which the map can be rendered
    • Provides the Graphics2D context
    • Provides the *RenderTargetState", i.e. which part of the map is visible
  2. ViewportState - describes which part of the map is visible.
    • Provides the coordinate-to-pixel transformation (and the inverse transformation, if possible)
    • Provides the map bounding box in the map CRS's coordinates
    • Provides the canvas size (this must get updated as soon as the widget is resized)
    • Provides map scale, zoom factor and total zoom
  3. MapContext - describes the Map with its MapLayers, each of which having one type of data
    (Or are there exceptions, with one layer holding, lets say, raster AND vector data?)
  4. RenderManager" - central object that starts/stops rendering, initiates rerendering and holds the MapRenderer that is called to render the map (or parts of it) when necessary.
  5. MapRenderer - renders the whole map (or a spatial subset), might delegate the work to specialized MapLayerRenderers
  6. MapLayerRenderer - specialized render that renders one map layer (or a spatial subset) when called to do so.

General ideas and assumtions

  • Rendering does not make sense without a RenderTarget (i.e. a MapCanvas, a printer or something like that)
  • A RenderTarget can show only one map at a time.
  • One instance of a RenderManager is needed for one RenderTarget
  • Renderers might either be stateless singletons, called and fed with all relevant information on demand...
    OR ... The RenderManager might create new instances of these Renderers as soon as "everything is put together", and these instances could be stateful, i.e. they could initialize things. For example it might be useful if the correct renderer for every map layer is chosen when the map is set (or changes) instead of everytime a map is rerendered. Stateful renderers could also store (and reuse) already rendered tiles or already extracted geometry data.
  • In Swing widgets, the Graphics (or Graphics2D) object is only available within the "paint" method, initiated by a call to "repaint". Basically this means, that the widget (i.e. the RenderTarget) must trigger repaint:
    1. 1. RenderTarget's repaint method gets called (automatically or programatically)
    2. 2. Internally the paint(Graphics) method is called.
    3. 3. This method gives the Graphics2D object and other information (ViewportState) to the MapRenderer
      AFAIK for SWT it can work equally. Does this make sense?

To be continued.

Use cases

Some UML diagram ideas