Class Tracer.Level
java.lang.Object
org.apache.sis.internal.processing.isoline.Tracer.Level
- Enclosing class:
- Tracer
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 Summary
FieldsModifier and TypeFieldDescriptionprivate final int
Band number where to read values in theTracer.window
array.(package private) int
Bitset telling which corners have a value greater than this isoline level value.Paths that have not yet been closed.private Joiner
Builder of isolines as a Java2D shape, created when first needed.private final PolylineBuffer
The polyline to be continued on the next column.private final PolylineBuffer[]
The polylines in each column which need to be continued on the next row.(package private) Shape
The isolines as a Java2D shape, created byfinish()
.(package private) final double
The level value. -
Constructor Summary
ConstructorsConstructorDescriptionLevel
(int band, double value, int width) Creates new isoline levels for the given value. -
Method Summary
Modifier and TypeMethodDescriptionprivate void
closeLeftWithTop
(PolylineBuffer polylineOnTop) JoinspolylineOnLeft
withpolylineOnTop
, saves their coordinates and clear thosePolylineBuffer
instances for use in next cell.(package private) final void
finish()
Invoked after the iteration has been completed on the full area of interest.(package private) final void
Invoked after iteration on a single row has been completed.(package private) final void
flush()
Flushes any pendingpartialPaths
topath
.(package private) final void
Adds segments computed for values in a single pixel.private double
interpolate
(int i1, int i2) Interpolates the position where the isoline passes between two values.private void
Appends topolylineOnLeft
a point interpolated on the left side if that point is missing.private void
interpolateMissingTopSide
(PolylineBuffer polylineOnTop) Appends topolylineOnTop
a point interpolated on the top side if that point is missing.private void
interpolateOnBottomSide
(PolylineBuffer polylineOnTop) Appends to the given polyline a point interpolated on the bottom side.private void
Appends topolylineOnLeft
a point interpolated on the right side.private void
interpolateOnTopSide
(PolylineBuffer appendTo) Appends to the given polyline a point interpolated on the top side.private boolean
Verifies thatpartialPaths
consistency.(package private) final void
merge
(Tracer.Level other) Transfers allother
polylines into this instance.(package private) final void
Initializes theisDataAbove
value with values for the column on the right side.(package private) final void
toRawPath
(Map<PolylineStage, Path2D> appendTo) Appends the pixel coordinates of this level to the given path, for debugging purposes only.private void
writeFragment
(PolylineBuffer polyline) Writes the content of given polyline without closing it as a polygon.
-
Field Details
-
band
private final int bandBand number where to read values in theTracer.window
array. -
value
final double valueThe level value.- See Also:
-
isDataAbove
int isDataAboveBitset 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 singlePolylineBuffer
, 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. -
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 fieldis empty
if the cell in previous iteration was like below (no line cross the right border): -
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 coordinatex
and values on the right border are given by pixels at coordinatex+1
. Example: -
partialPaths
Paths that have not yet been closed. ThePolylineBuffer
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 thecloseLeftWithTop(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 ofdouble[]
arrays to be concatenated in a single polygon later. For a givenFragments
list, alldouble[]
arrays at even indices shall have their points read in reverse order and alldouble[]
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
Builder of isolines as a Java2D shape, created when first needed. ThePolylineBuffer
coordinates are copied in this path when a geometry is closed and transformed usingTracer.gridToCRS
. This is almost final result; the only difference compared toshape
is that the coordinates are not yet wrapped in aShape
. -
shape
Shape shapeThe isolines as a Java2D shape, created byfinish()
. 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 theTracer.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 theisDataAbove
value with values for the column on the right side. After this method call, theTracer.UPPER_RIGHT
andTracer.LOWER_RIGHT
bits still need to be set.- See Also:
-
interpolate
final void interpolate() throws org.opengis.referencing.operation.TransformExceptionAdds segments computed for values in a single pixel. Interpolations are determined by the 4 lowest bits ofisDataAbove
. ThepolylineOnLeft
andpolylinesOnTop[x]
elements are updated by this method.How NaN values are handled
This algorithm does not need special attention forDouble.NaN
values. Interpolations will produceNaN
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 thePathBuilder
.- Throws:
org.opengis.referencing.operation.TransformException
-
interpolateMissingLeftSide
private void interpolateMissingLeftSide()Appends topolylineOnLeft
a point interpolated on the left side if that point is missing. This interpolation should happens only in the first column. -
interpolateMissingTopSide
Appends topolylineOnTop
a point interpolated on the top side if that point is missing. This interpolation should happens only in the first row. -
interpolateOnTopSide
Appends to the given polyline a point interpolated on the top side. -
interpolateOnRightSide
private void interpolateOnRightSide()Appends topolylineOnLeft
a point interpolated on the right side. The polyline on right side will becomepolylineOnLeft
in next column. -
interpolateOnBottomSide
Appends to the given polyline a point interpolated on the bottom side. The polyline on top side will become apolylineOnBottoù
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 JoinspolylineOnLeft
withpolylineOnTop
, saves their coordinates and clear thosePolylineBuffer
instances for use in next cell. The coordinates are written directly topath
if we got a closed polygon, or otherwise are saved inpartialPaths
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 polylinespolylineOnLeft
andpolylineOnTop
will become empty after this method call.- Parameters:
polylineOnTop
- value ofpolylinesOnTop[x]
.- Throws:
org.opengis.referencing.operation.TransformException
- if theTracer.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.TransformExceptionInvoked 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 thatPolylineBuffer
will not be continued by cells on next rows.- Throws:
org.opengis.referencing.operation.TransformException
-
finish
final void finish() throws org.opengis.referencing.operation.TransformExceptionInvoked after the iteration has been completed on the full area of interest. This method writes all remaining polylines topartialPaths
. It assumes thatfinishedRow()
has already been invoked. ThisTracer.Level
instance cannot be used anymore after this call.- Throws:
org.opengis.referencing.operation.TransformException
-
isConsistent
private boolean isConsistent()Verifies thatpartialPaths
consistency. Used for assertions only. -
merge
Transfers allother
polylines into this instance. Theother
instance should be a neighbor, i.e. an instance sharing a border with this instance. Theother
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.TransformExceptionFlushes any pendingpartialPaths
topath
. This method is invoked afterfinish()
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
Appends the pixel coordinates of this level to the given path, for debugging purposes only. TheTracer.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:
-