Class Tracer.Level

java.lang.Object
org.apache.sis.internal.processing.isoline.Tracer.Level
Enclosing class:
Tracer

final class Tracer.Level extends Object
Builder of polylines for a single level. The segments to create are determined by a set of four flags (one for each corner) encoded in an integer. The meaning of those flags is described in Wikipedia "Marching squares" article, except that this implementation uses different values.
  • Field Details

    • band

      private final int band
      Band number where to read values in the Tracer.window array.
    • value

      final double value
      The level value.
      See Also:
    • isDataAbove

      int isDataAbove
      Bitset telling which corners have a value greater than this isoline level value. Each corner is associated to one of the bits illustrated below, where bit (0) is the less significant. Note that this bit order is different than the order used in Wikipedia "Marching squares" article. The order used in this class allows more direct bitwise operations as described in next section. Bits are set to 1 where the data value is above the isoline value, and 0 where the data value is below the isoline value. Data values exactly equal to the isoline value are handled as if they were greater. It does not matter for interpolations: we could flip this convention randomly, the interpolated points would still be the same. It could change the way line segments are assembled in a single PolylineBuffer, but the algorithm stay consistent if we always apply the same rule for all points.

      Reusing bits from previous iteration

      We will iterate on pixels from left to right, then from top to bottom. With that iteration order, bits 0 and 2 can be obtained from the bit pattern of previous iteration with a simple bit shift.
      See Also:
    • polylineOnLeft

      private final PolylineBuffer polylineOnLeft
      The polyline to be continued on the next column. This is a single instance because iteration happens from left to right before top to bottom. This instance is non-empty if the cell in previous iteration was like below (all those examples have a line crossing the right border): This field is empty if the cell in previous iteration was like below (no line cross the right border):
    • polylinesOnTop

      private final PolylineBuffer[] polylinesOnTop
      The polylines in each column which need to be continued on the next row. This array contains empty instances in columns where there are no polylines to continue on next row. For non-empty element at index x, values on the left border are given by pixels at coordinate x and values on the right border are given by pixels at coordinate x+1. Example:
    • partialPaths

      private final Map<Point,Fragments> partialPaths
      Paths that have not yet been closed. The PolylineBuffer coordinates are copied in this map when iteration finished on a row but the polyline under construction will not be continued by the next row, or when the closeLeftWithTop(PolylineBuffer) method has been invoked but the geometry to close is still not complete. This map accumulates those partial shapes for assembling them later when missing parts become available.

      Map keys

      Keys are grid coordinates rounded toward 0. The coordinate having fraction digits has its bits inverted by the ~ operator. For each point, there is at most one coordinate having such fraction digits.

      Map values

      Fragments instances are list of double[] arrays to be concatenated in a single polygon later. For a given Fragments list, all double[] arrays at even indices shall have their points read in reverse order and all double[] arrays at odd indices shall have their points read in forward order. The list may contain null elements when there is no data in the corresponding iteration order.
      See Also:
    • path

      private Joiner path
      Builder of isolines as a Java2D shape, created when first needed. The PolylineBuffer coordinates are copied in this path when a geometry is closed and transformed using Tracer.gridToCRS. This is almost final result; the only difference compared to shape is that the coordinates are not yet wrapped in a Shape.
      See Also:
    • shape

      Shape shape
      The isolines as a Java2D shape, created by finish(). This is the shape to be returned to user for this level after we finished to process all cells.
      See Also:
  • Constructor Details

    • Level

      Level(int band, double value, int width)
      Creates new isoline levels for the given value.
      Parameters:
      band - band number where to read values in the Tracer.window array.
      value - the isoline level value.
      width - the contouring grid cell width (one cell smaller than image width).
  • Method Details

    • nextColumn

      final void nextColumn()
      Initializes the isDataAbove value with values for the column on the right side. After this method call, the Tracer.UPPER_RIGHT and Tracer.LOWER_RIGHT bits still need to be set.
      See Also:
    • interpolate

      final void interpolate() throws org.opengis.referencing.operation.TransformException
      Adds segments computed for values in a single pixel. Interpolations are determined by the 4 lowest bits of isDataAbove. The polylineOnLeft and polylinesOnTop[x] elements are updated by this method.

      How NaN values are handled

      This algorithm does not need special attention for Double.NaN values. Interpolations will produce NaN values and append them to the correct polyline (which does not depend on interpolation result) like real values. Those NaN values will be filtered later in another method, when copying coordinates in the PathBuilder.
      Throws:
      org.opengis.referencing.operation.TransformException
    • interpolateMissingLeftSide

      private void interpolateMissingLeftSide()
      Appends to polylineOnLeft a point interpolated on the left side if that point is missing. This interpolation should happens only in the first column.
    • interpolateMissingTopSide

      private void interpolateMissingTopSide(PolylineBuffer polylineOnTop)
      Appends to polylineOnTop a point interpolated on the top side if that point is missing. This interpolation should happens only in the first row.
    • interpolateOnTopSide

      private void interpolateOnTopSide(PolylineBuffer appendTo)
      Appends to the given polyline a point interpolated on the top side.
    • interpolateOnRightSide

      private void interpolateOnRightSide()
      Appends to polylineOnLeft a point interpolated on the right side. The polyline on right side will become polylineOnLeft in next column.
    • interpolateOnBottomSide

      private void interpolateOnBottomSide(PolylineBuffer polylineOnTop)
      Appends to the given polyline a point interpolated on the bottom side. The polyline on top side will become a polylineOnBottoù in next row.
    • interpolate

      private double interpolate(int i1, int i2)
      Interpolates the position where the isoline passes between two values.
      Parameters:
      i1 - index of first value in the buffer, ignoring band offset.
      i2 - index of second value in the buffer, ignoring band offset.
      Returns:
      a value interpolated between the values at the two given indices.
    • closeLeftWithTop

      private void closeLeftWithTop(PolylineBuffer polylineOnTop) throws org.opengis.referencing.operation.TransformException
      Joins polylineOnLeft with polylineOnTop, saves their coordinates and clear those PolylineBuffer instances for use in next cell. The coordinates are written directly to path if we got a closed polygon, or otherwise are saved in partialPaths for later processing. This method is invoked for cells like below: This method does itself the interpolations on left side and top side. The two polylines polylineOnLeft and polylineOnTop will become empty after this method call.
      Parameters:
      polylineOnTop - value of polylinesOnTop[x].
      Throws:
      org.opengis.referencing.operation.TransformException - if the Tracer.gridToCRS transform cannot be applied.
    • writeFragment

      private void writeFragment(PolylineBuffer polyline) throws org.opengis.referencing.operation.TransformException
      Writes the content of given polyline without closing it as a polygon. The given polyline will become empty after this method call.
      Throws:
      org.opengis.referencing.operation.TransformException
    • finishedRow

      final void finishedRow() throws org.opengis.referencing.operation.TransformException
      Invoked after iteration on a single row has been completed. If there is a polyline finishing on the right image border, the coordinates needs to be saved somewhere because that PolylineBuffer will not be continued by cells on next rows.
      Throws:
      org.opengis.referencing.operation.TransformException
    • finish

      final void finish() throws org.opengis.referencing.operation.TransformException
      Invoked after the iteration has been completed on the full area of interest. This method writes all remaining polylines to partialPaths. It assumes that finishedRow() has already been invoked. This Tracer.Level instance cannot be used anymore after this call.
      Throws:
      org.opengis.referencing.operation.TransformException
    • isConsistent

      private boolean isConsistent()
      Verifies that partialPaths consistency. Used for assertions only.
    • merge

      final void merge(Tracer.Level other) throws org.opengis.referencing.operation.TransformException
      Transfers all other polylines into this instance. The other instance should be a neighbor, i.e. an instance sharing a border with this instance. The other instance will become empty after this method call.
      Parameters:
      other - a neighbor level (on top, left, right or bottom) to merge with this level.
      Throws:
      org.opengis.referencing.operation.TransformException - if an error occurred during polylines creation.
    • flush

      final void flush() throws org.opengis.referencing.operation.TransformException
      Flushes any pending partialPaths to path. This method is invoked after finish() has been invoked for all sub-regions (many sub-regions may exist if isoline generation has been splitted for parallel computation).
      Throws:
      org.opengis.referencing.operation.TransformException - if an error occurred during polylines creation.
    • toRawPath

      @Debug final void toRawPath(Map<PolylineStage,Path2D> appendTo)
      Appends the pixel coordinates of this level to the given path, for debugging purposes only. The Tracer.gridToCRS transform is not applied by this method. For avoiding confusing behavior, that transform should be null.
      Parameters:
      appendTo - where to append the coordinates.
      See Also: