Class Canvas

Object
Canvas
All Implemented Interfaces:
Localized
Direct Known Subclasses:
Planar­Canvas

public class Canvas extends Object implements Localized
Common abstraction for implementations that manage the display and user manipulation of spatial graphic elements. This base class makes no assumption about the geometry of the display device (e.g. flat video monitor using Cartesian coordinate system, or planetarium dome using spherical coordinate system).

This Canvas base class does not draw anything by itself. Subclasses are responsible for drawing graphic elements. The visual contents are usually geographic located symbols, features or images, but some implementations can also manage non-geographic elements like a map scale.

A Canvas manages four fundamental properties:

  • The coordinate reference system to use for displaying data.
  • The location of data to display in all dimensions, including the dimensions not shown by the display device (for example time).
  • The size of the display device, in units of the display coordinate system (typically pixels).
  • The conversion from the Coordinate Reference System to the display coordinate system.
Those properties are explained in more details below. Other information, for example the geographic bounding box of the data shown on screen, are inferred from above properties.

Coordinate Reference Systems

There is three Coordinate Reference Systems involved in the rendering of geospatial data:
  1. The data CRS is specific to the data to be displayed. It may be anything convertible to the objective CRS. Different graphic elements may use different data CRS, potentially with a different number of dimensions.
  2. The objective CRS is the common CRS in which all data are converted before to be displayed. If the objective CRS involves a map projection, it determines the deformation of shapes that user will see on the display device. The objective CRS should have the same number of dimensions than the display device (often 2). Its domain of validity should be wide enough for encompassing all data. The CRS​.suggest­Common­Target(…) method may be helpful for choosing an objective CRS from a set of data CRS.
  3. The display CRS is the coordinate system of the display device. The conversion from objective CRS to display CRS should be an affine transform with a scale, a translation and optionally a rotation. This conversion changes every time that the user zooms or scrolls on viewed data.

Location of data to display

In addition of above-cited Coordinate Reference Systems, a Canvas also contains a point of interest. The point of interest is often, but not necessarily, at the center of display area. It defines the position where resolutions will be computed, and the position to keep fixed when scales and rotations are applied.

The point of interest can be expressed in any CRS; it does not need to be the objective CRS or the CRS of any data. However, the CRS of that point must have enough dimensions for being convertible to the CRS of all data. This rule implies that the number of dimensions of the point of interest is equal or greater than the highest number of dimensions found in data. The purpose is not only to specify which point to show in (typically) the center of the display area, but also to specify which slice to select in all dimensions not shown by the display device.

Example

If some data have (x,y,z) dimensions and other data have (x,y,t) dimensions, then the point of interest shall contain coordinate values for at least all of the (x,y,z,t) dimensions (i.e. it must be 4-dimensional, even if all data in this example are 3-dimensional). If the display device is a two-dimensional screen showing map in the (x,y) dimensions (horizontal plane), then the point of interest defines the z value (elevation or depth) and the t value (date and time) of the slice to show.

Display device size

The geographic extent of data to be rendered is constrained by the zoom level and the display device size. The display size is given by get­Display­Bounds() as an envelope having the number of dimensions of the display device. The display bounds is usually given in pixel units, but other units such as Units​.POINT are also authorized. The zoom level is given indirectly by the get­Objective­To­Display() transform. The display device may have a wraparound axis, for example in the spherical coordinate system of a planetarium.

Multi-threading

Canvas is not thread-safe. Synchronization, if desired, must be done by the caller. Another common strategy is to interact with Canvas from a single thread, for example the Swing or JavaFX event queue.
Since:
1.1
  • Field Details

  • Constructor Details

    • Canvas

      protected Canvas(EngineeringCRS displayCRS, Locale locale)
      Creates a new canvas for a display device using the given coordinate reference system. The display CRS of a canvas cannot be changed after construction. Its coordinate system depends on the display device shape (for example a two-dimensional Cartesian coordinate system for flat screens, or a polar or spherical coordinate system for planetarium domes). The axis units of measurement are typically (but not necessarily) Units​.PIXEL for Cartesian coordinate systems, with Units​.DEGREE in polar, cylindrical or spherical coordinate systems.
      Parameters:
      display­CRS - the coordinate system of the display device.
      locale - the locale to use for labels and some messages, or null for default.
  • Method Details

    • getLocale

      public Locale getLocale()
      Returns the locale used for texts or for producing some error messages. May be null if no locale has been specified, in which case the system default should be used.
      Specified by:
      get­Locale in interface Localized
      Returns:
      the locale for messages, or null if not explicitly defined.
    • getDisplayCRS

      public final EngineeringCRS getDisplayCRS()
      Returns the Coordinate Reference System of the display device. The axis units of measurement are typically (but not necessarily) Units​.PIXEL for Cartesian coordinate systems, with Units​.DEGREE in polar, cylindrical or spherical coordinate systems. The coordinate system may have a wraparound axis for some "exotic" display devices (e.g. planetarium dome).

      Note that the CRS​.find­Operation(…) static method can generally not handle this display CRS. To apply coordinate operations on display coordinates, get­Objective­To­Display() transform must be inverted and used.

      Usage note

      Invoking this method is rarely needed. It is sufficient to said that a display CRS exists at least conceptually, and that we define a conversion from the objective CRS to that display CRS. This method may be useful when the subclasses may be something else than Planar­Canvas, in which case the caller may want more information about the geometry of the display device.
      Returns:
      the Coordinate Reference System of the display device.
      See Also:
    • getObjectiveCRS

      public CoordinateReferenceSystem getObjectiveCRS()
      Returns the Coordinate Reference System in which all data are transformed before displaying. After conversion to this CRS, coordinates should be related to the display device coordinates with only a final scale, a translation and optionally a rotation remaining to apply.

      This value may be null on newly created Canvas, before data are added and canvas is configured. It should not be null anymore once a Canvas is ready for displaying.

      Returns:
      the Coordinate Reference System in which to transform all data before displaying.
      See Also:
    • setObjectiveCRS

      public void setObjectiveCRS(CoordinateReferenceSystem newValue, DirectPosition anchor) throws RenderException
      Sets the Coordinate Reference System in which all data are transformed before displaying. The new CRS must be compatible with the previous CRS, i.e. a coordinate operation between the two CRSs shall exist. If this is not the case (e.g. for rendering completely new data), use set­Grid­Geometry(Grid­Geometry) instead.

      The given CRS should have a domain of validity wide enough for encompassing all data (the CRS​.suggest­Common­Target(…) method may be helpful for choosing an objective CRS from a set of data CRS). If the given value is different than the previous value, then a change event is sent to all listeners registered for the "objectiveCRS" property.

      If the transform between old and new CRS is not identity, then this method recomputes the objective to display conversion in a way preserving the display coordinates of the given anchor, together with the scales and orientations of features in close neighborhood of that point. This calculation may cause "objectiveToDisplay" property change event with the Transform­Change­Event​.Reason​.CRS_CHANGE reason to be sent to listeners. That event is sent after the above-cited "objectiveCRS" event (note that "pointOfInterest" stay unchanged). All those change events are sent only after all property values have been updated to their new values.

      Parameters:
      new­Value - the new Coordinate Reference System in which to transform all data before displaying.
      anchor - the point to keep at fixed display coordinates, expressed in any compatible CRS. If null, defaults to point of interest. If non-null, the anchor must be associated to a CRS.
      Throws:
      Null­Pointer­Exception - if the given CRS is null.
      Mismatched­Dimension­Exception - if the given CRS does not have the number of dimensions of the display device.
      Render­Exception - if the objective CRS cannot be set to the given value for another reason.
    • getObjectiveToDisplay

      public LinearTransform getObjectiveToDisplay()
      Returns the (usually affine) conversion from objective CRS to display coordinate system. The source coordinates shall be in the CRS given by get­Objective­CRS() and the converted coordinates will be in the CRS given by get­Display­CRS().

      The objective to display conversion changes every time that user zooms or scrolls on viewed data. However, the transform returned by this method is a snapshot taken at the time this method is invoked; subsequent changes in the objective to display conversion are not reflected in the returned transform.

      Returns:
      snapshot of the (usually affine) conversion from objective CRS to display coordinate system (never null).
      See Also:
    • setObjectiveToDisplay

      public void setObjectiveToDisplay(LinearTransform newValue) throws RenderException
      Sets the conversion from objective CRS to display coordinate system. If the given value is different than the previous value, then a change event is sent to all listeners registered for the "objectiveToDisplay" property. The event reason is Transform­Change­Event​.Reason​.ASSIGNMENT.

      Invoking this method has the effect of changing the viewed area, the zoom level or the rotation of the map. It does not update the "pointOfInterest" property however. The point of interest may move outside the view area as a result of this method call.

      Parameters:
      new­Value - the new objective to display conversion.
      Throws:
      Illegal­Argument­Exception - if given the transform does not have the expected number of dimensions or is not affine.
      Render­Exception - if the objective to display transform cannot be set to the given value for another reason.
    • getDisplayBounds

      public Envelope getDisplayBounds()
      Returns the size and location of the display device. The unit of measurement is typically (but not necessarily) pixels. The coordinate values are often integers, but this is not mandatory. The coordinate reference system is given by get­Display­CRS().

      This value may be null on newly created Canvas, before data are added and canvas is configured. It should not be null anymore once a Canvas is ready for displaying.

      Returns:
      size and location of the display device.
      See Also:
    • setDisplayBounds

      public void setDisplayBounds(Envelope newValue) throws RenderException
      Sets the size and location of the display device. The envelope CRS shall be either the display CRS or unspecified, in which case the display CRS is assumed. Unit of measurement is typically (but not necessarily) Units​.PIXEL. If the given value is different than the previous value, then a change event is sent to all listeners registered for the "displayBounds" property.
      Parameters:
      new­Value - the new display bounds.
      Throws:
      Illegal­Argument­Exception - if the given envelope does not have the expected CRS or number of dimensions.
      Render­Exception - if the display bounds cannot be set to the given value for another reason.
    • getPointOfInterest

      public DirectPosition getPointOfInterest(boolean objective)
      Returns the coordinates of a point considered representative of the data. This is typically (but not necessarily) the center of data bounding box. This point is used for example as the default location where to compute resolution (the resolution may vary at each pixel because of map projection deformations). This position may become outside the viewing area after zooms or translations have been applied.

      The coordinates can be given in their original CRS or in the objective CRS. If objective is false, then the returned position can be expressed in any CRS convertible to data or objective CRS. If that CRS has more dimensions than the objective CRS, then the supplemental dimensions specify which slice to show (for example the depth of the horizontal plane to display, or the date of the dynamic phenomenon to display. See class javadoc for more discussion.) If objective is true, then the position is transformed to the objective CRS.

      This value is initially null. A value should be specified either by invoking set­Point­Of­Interest(Direct­Position) or set­Grid­Geometry(Grid­Geometry).

      Parameters:
      objective - whether to return a position transformed to objective CRS.
      Returns:
      coordinates of a representative point, or null if unspecified.
      See Also:
    • setPointOfInterest

      public void setPointOfInterest(DirectPosition newValue) throws RenderException
      Sets the coordinates of a representative point inside the data bounding box. If the given value is different than the previous value, then a change event is sent to all listeners registered for the "pointOfInterest" property.
      Parameters:
      new­Value - the new coordinates of a representative point.
      Throws:
      Null­Pointer­Exception - if the given position is null.
      Illegal­Argument­Exception - if the given position does not have a CRS.
      Render­Exception - if the point of interest cannot be set to the given value.
    • getGridGeometry

      public GridGeometry getGridGeometry() throws RenderException
      Returns canvas properties (CRS, display bounds, conversion) encapsulated in a grid geometry. This is a convenience method for interoperability with grid coverage API. If set­Grid­Geometry(Grid­Geometry) has been invoked with a non-null value and no other Canvas property changed since that method call, then this method returns that value. Otherwise this method computes a grid geometry as described below.

      The set of Grid­Geometry dimensions includes all the dimensions of the objective CRS, augmented with all (if possible) or some supplemental dimensions found in the point of interest. For example if the canvas manages only (x,y) coordinates but the point of interest includes also a t coordinate, then a third dimension (which we call the supplemental dimension) for t is added to the CRS, Grid­Extent and "grid to CRS" transform of the returned grid geometry.

      Canvas properties → grid geometry properties
      Grid geometry element Display dimensions Supplemental dimensions
      GridGeometry.getCoordinateReferenceSystem() getObjectiveCRS(). Some of get­Point­Of­Interest(false)​.get­Coordinate­Reference­System()
      GridGeometry.getExtent() getDisplayBounds() rounded to enclosing (floor and ceil) integers [0 … 0]
      GridGeometry.getGridToCRS(PixelInCell) Inverse of get­Objective­To­Display() Some point of interest coordinates as translation terms
      The Grid­Geometry​.get­Grid­To­CRS(Pixel­In­Cell) transform built by this method is always a Linear­Transform. This linearity implies that the grid geometry CRS cannot be the Point Of Interest (POI) CRS, unless conversion from POI CRS to objective CRS is linear.
      Returns:
      a grid geometry encapsulating canvas properties, including supplemental dimensions if possible.
      Throws:
      Render­Exception - if the grid geometry cannot be computed.
    • setGridGeometry

      public void setGridGeometry(GridGeometry newValue) throws RenderException
      Sets canvas properties from the given grid geometry. This convenience method converts the coordinate reference system, "grid to CRS" transform and extent of the given grid geometry to Canvas properties. If the given value is different than the previous value, then change events are sent to all listeners registered for the "displayBounds", "objectiveCRS", "objectiveToDisplay" (with Transform­Change­Event​.Reason​.GRID_GEOMETRY_CHANGE reason), and/or "pointOfInterest" properties, in that order.

      The value given to this method will be returned by get­Grid­Geometry() as long as none of above cited properties is changed. If one of those properties changes (for example if the user zooms or pans the map), then a new grid geometry will be computed. There is no guarantee that the recomputed grid geometry will be similar to the grid geometry specified to this method. For example, the Grid­Extent in supplemental dimensions may be different.

      Parameters:
      new­Value - the grid geometry from which to get new canvas properties.
      Throws:
      Render­Exception - if the given grid geometry cannot be converted to canvas properties.
    • getGeographicArea

      public Optional<GeographicBoundingBox> getGeographicArea() throws RenderException
      Returns the geographic bounding box encompassing the area shown on the display device. If the objective CRS is not convertible to a geographic CRS, then this method returns an empty value.
      Returns:
      geographic bounding box encompassing the viewed area.
      Throws:
      Render­Exception - in an error occurred while computing the geographic area.
      See Also:
    • getSpatialResolution

      public OptionalDouble getSpatialResolution() throws RenderException
      Returns an estimation of the resolution (in metres) at the point of interest. If the objective CRS is not convertible to a geographic CRS, then this method returns an empty value.
      Returns:
      estimation of the resolution in metres at current point of interest.
      Throws:
      Render­Exception - in an error occurred while computing the resolution.
    • addPropertyChangeListener

      public final void addPropertyChangeListener(String propertyName, PropertyChangeListener listener)
      Registers a listener for the property of the given name. The listener will be notified every time that the property of the given name got a new value. If the same listener is registered twice for the same property, then it will be notified twice (this method does not perform duplication checks).
      Parameters:
      property­Name - name of the property to listen (should be one of the *_PROPERTY constants).
      listener - property listener to register.
    • removePropertyChangeListener

      public final void removePropertyChangeListener(String propertyName, PropertyChangeListener listener)
      Unregisters a property listener. The given property­Name should be the name used during listener registration. If the specified listener is not registered for the named property, then nothing happen. If the listener has been registered twice, then only one registration is removed (one registration will remain).
      Parameters:
      property­Name - name of the listened property.
      listener - property listener to unregister.
    • hasPropertyChangeListener

      protected final boolean hasPropertyChangeListener(String propertyName)
      Returns true if the given property has at least one listener.
      Parameters:
      property­Name - name of the property to test.
      Returns:
      true if the given property has at least one listener.
    • firePropertyChange

      protected void firePropertyChange(String propertyName, Object oldValue, Object newValue)
      Notifies all registered listeners that a property of the given name changed its value. The change event source will be this. It is caller responsibility to verify that the old and new values are different (this method does not check for equality).
      Parameters:
      property­Name - name of the property that changed its value.
      old­Value - the old property value (may be null).
      new­Value - the new property value (may be null).
      See Also:
    • firePropertyChange

      protected void firePropertyChange(PropertyChangeEvent event)
      Notifies all registered listeners that a property changed its value. It is caller responsibility to verify that the event source and property name are valid.
      Parameters:
      event - the event to forward. Cannot be null.
      See Also: