From 341e6f3ae6bb489e88fa0d051d2409b1a48d240a Mon Sep 17 00:00:00 2001 From: Eugene Maksymenko Date: Sat, 23 Dec 2023 15:14:04 +0200 Subject: [PATCH] Add Graticule line style support. --- .../layer/graticule/AbstractGraticuleLayer.kt | 90 +++++-------------- .../graticule/GraticuleRenderingParams.kt | 25 ++---- .../layer/graticule/GraticuleSupport.kt | 33 +++---- .../worldwind/layer/graticule/LineStyle.kt | 20 +++++ 4 files changed, 61 insertions(+), 107 deletions(-) create mode 100644 worldwind/src/commonMain/kotlin/earth/worldwind/layer/graticule/LineStyle.kt diff --git a/worldwind/src/commonMain/kotlin/earth/worldwind/layer/graticule/AbstractGraticuleLayer.kt b/worldwind/src/commonMain/kotlin/earth/worldwind/layer/graticule/AbstractGraticuleLayer.kt index a46f58afd..2cee707dd 100644 --- a/worldwind/src/commonMain/kotlin/earth/worldwind/layer/graticule/AbstractGraticuleLayer.kt +++ b/worldwind/src/commonMain/kotlin/earth/worldwind/layer/graticule/AbstractGraticuleLayer.kt @@ -43,26 +43,6 @@ abstract class AbstractGraticuleLayer(name: String): AbstractLayer(name) { // private var terrainConformance = 50.0 companion object { -// /** -// * Solid line rendering style. This style specifies that a line will be drawn without any breaks.

-// *
`_________`
-// *

is an example of a solid line. -// */ -// val LINE_STYLE_SOLID = GraticuleRenderingParams.VALUE_LINE_STYLE_SOLID -// /** -// * Dashed line rendering style. This style specifies that a line will be drawn as a series of long strokes, with -// * space in between.

-// *
`- - - - -`
-// *

is an example of a dashed line. -// */ -// val LINE_STYLE_DASHED = GraticuleRenderingParams.VALUE_LINE_STYLE_DASHED -// /** -// * Dotted line rendering style. This style specifies that a line will be drawn as a series of evenly spaced "square" -// * dots.

-// *
`. . . . .`
-// * is an example of a dotted line. -// */ -// val LINE_STYLE_DOTTED = GraticuleRenderingParams.VALUE_LINE_STYLE_DOTTED private const val LOOK_AT_LATITUDE_PROPERTY = "look_at_latitude" private const val LOOK_AT_LONGITUDE_PROPERTY = "look_at_longitude" private const val GRATICULE_PIXEL_SIZE_PROPERTY = "graticule_pixel_size" @@ -124,25 +104,25 @@ abstract class AbstractGraticuleLayer(name: String): AbstractLayer(name) { * @param lineWidth width of the graticule lines. * @param key the rendering parameters key. */ - fun setGraticuleLineWidth(lineWidth: Double, key: String) { getRenderingParams(key).lineWidth = lineWidth } - -// /** -// * Returns the graticule line rendering style. -// * -// * @param key the rendering parameters key. -// * -// * @return rendering style of the graticule lines. -// */ -// fun getGraticuleLineStyle(key: String) = getRenderingParams(key).lineStyle -// -// /** -// * Sets the graticule line rendering style. -// * -// * @param lineStyle rendering style of the graticule lines. One of LINE_STYLE_SOLID, LINE_STYLE_DASHED, or -// * LINE_STYLE_DOTTED. -// * @param key the rendering parameters key. -// */ -// fun setGraticuleLineStyle(lineStyle: String, key: String) { getRenderingParams(key).lineStyle = lineStyle } + fun setGraticuleLineWidth(lineWidth: Float, key: String) { getRenderingParams(key).lineWidth = lineWidth } + + /** + * Returns the graticule line rendering style. + * + * @param key the rendering parameters key. + * + * @return rendering style of the graticule lines. + */ + fun getGraticuleLineStyle(key: String) = getRenderingParams(key).lineStyle + + /** + * Sets the graticule line rendering style. + * + * @param lineStyle rendering style of the graticule lines. + * One of [LineStyle.SOLID], [LineStyle.DASHED], [LineStyle.DOTTED] or [LineStyle.DASH_DOTTED]. + * @param key the rendering parameters key. + */ + fun setGraticuleLineStyle(lineStyle: LineStyle, key: String) { getRenderingParams(key).lineStyle = lineStyle } /** * Returns whether graticule labels will be rendered. @@ -267,29 +247,8 @@ abstract class AbstractGraticuleLayer(name: String): AbstractLayer(name) { lastVerticalExaggeration = rc.verticalExaggeration // lastGlobe = rc.globe // if (rc.is2DGlobe) lastProjection = (rc.globe as Globe2D).projection -// terrainConformance = computeTerrainConformance(rc) -// applyTerrainConformance() } -// private fun computeTerrainConformance(rc: RenderContext): Double { -// var value = 100 -// val alt = rc.camera.position.altitude -// when { -// alt < 10e3 -> value = 20 -// alt < 50e3 -> value = 30 -// alt < 100e3 -> value = 40 -// alt < 1000e3 -> value = 60 -// } -// return value.toDouble() -// } -// -// private fun applyTerrainConformance() { -// val graticuleType = getOrderedTypes() -// for (type in graticuleType) { -// getRenderingParams(type)[GraticuleRenderingParams.KEY_LINE_CONFORMANCE] = terrainConformance -// } -// } - fun computeLabelOffset(rc: RenderContext): Location { return if (hasLookAtPos(rc)) { val labelOffsetDegrees = getLabelOffset(rc) @@ -307,9 +266,8 @@ abstract class AbstractGraticuleLayer(name: String): AbstractLayer(name) { fun createLineRenderable(positions: List, pathType: PathType) = Path(positions).apply { this.pathType = pathType - isFollowTerrain = true - // terrainConformance = 1.0 // TODO Why not terrainConformance? altitudeMode = AltitudeMode.CLAMP_TO_GROUND + isFollowTerrain = true } @Suppress("UNUSED_PARAMETER") @@ -360,9 +318,9 @@ abstract class AbstractGraticuleLayer(name: String): AbstractLayer(name) { fun computeTruncatedSegment(p1: Position, p2: Position, sector: Sector, positions: MutableList) { val p1In = sector.contains(p1.latitude, p1.longitude) val p2In = sector.contains(p2.latitude, p2.longitude) - if (!p1In && !p2In) return // whole segment is (likely) outside + if (!p1In && !p2In) return // the whole segment is (likely) outside if (p1In && p2In) { - // whole segment is (likely) inside + // the whole segment is (likely) inside positions.add(p1) positions.add(p2) } else { @@ -422,7 +380,7 @@ abstract class AbstractGraticuleLayer(name: String): AbstractLayer(name) { val deltaLon = getDeltaLongitude(p1, p2.longitude) if (getDeltaLongitude(p1, longitude) < deltaLon && getDeltaLongitude(p2, longitude) < deltaLon) { var count = 0 - val precision = 1.0 / 6378137.0 // 1m angle in radians + val precision = 1.0 / Ellipsoid.WGS84.semiMajorAxis // 1m angle in radians var a = p1 var b = p2 var midPoint = greatCircleMidPoint(a, b) @@ -451,7 +409,7 @@ abstract class AbstractGraticuleLayer(name: String): AbstractLayer(name) { var pos: Location? = null if (sign(p1.latitude.inDegrees - latitude.inDegrees) != sign(p2.latitude.inDegrees - latitude.inDegrees)) { var count = 0 - val precision = 1.0 / 6378137.0 // 1m angle in radians + val precision = 1.0 / Ellipsoid.WGS84.semiMajorAxis // 1m angle in radians var a = p1 var b = p2 var midPoint = greatCircleMidPoint(a, b) diff --git a/worldwind/src/commonMain/kotlin/earth/worldwind/layer/graticule/GraticuleRenderingParams.kt b/worldwind/src/commonMain/kotlin/earth/worldwind/layer/graticule/GraticuleRenderingParams.kt index 1620b3802..b75e18788 100644 --- a/worldwind/src/commonMain/kotlin/earth/worldwind/layer/graticule/GraticuleRenderingParams.kt +++ b/worldwind/src/commonMain/kotlin/earth/worldwind/layer/graticule/GraticuleRenderingParams.kt @@ -10,12 +10,12 @@ class GraticuleRenderingParams: MutableMap by HashMap() { var lineColor: Color? get() = get(KEY_LINE_COLOR) as? Color set(color) { put(KEY_LINE_COLOR, color) } - var lineWidth: Double - get() = get(KEY_LINE_WIDTH) as? Double ?: 0.0 + var lineWidth: Float + get() = get(KEY_LINE_WIDTH) as? Float ?: 0.0f set(lineWidth) { put(KEY_LINE_WIDTH, lineWidth) } -// var lineStyle: String? -// get() = get(KEY_LINE_STYLE) as? String -// set(lineStyle) { put(KEY_LINE_STYLE, lineStyle) } + var lineStyle: LineStyle + get() = get(KEY_LINE_STYLE) as? LineStyle ?: LineStyle.SOLID + set(lineStyle) { put(KEY_LINE_STYLE, lineStyle) } var isDrawLabels: Boolean get() = get(KEY_DRAW_LABELS) as? Boolean ?: false set(drawLabels) { put(KEY_DRAW_LABELS, drawLabels) } @@ -26,26 +26,13 @@ class GraticuleRenderingParams: MutableMap by HashMap() { get() = get(KEY_LABEL_FONT) as? Font set(font) { put(KEY_LABEL_FONT, font) } - fun getStringValue(key: String) = this[key]?.toString() - - fun getFloatValue(key: String): Float? { - val o = get(key) ?: return null - if (o is Float) return o - val v = getStringValue(key) - return v?.toFloat() - } - companion object { const val KEY_DRAW_LINES = "DrawGraticule" const val KEY_LINE_COLOR = "GraticuleLineColor" const val KEY_LINE_WIDTH = "GraticuleLineWidth" -// const val KEY_LINE_STYLE = "GraticuleLineStyle"; -// const val KEY_LINE_CONFORMANCE = "GraticuleLineConformance"; + const val KEY_LINE_STYLE = "GraticuleLineStyle"; const val KEY_DRAW_LABELS = "DrawLabels" const val KEY_LABEL_COLOR = "LabelColor" const val KEY_LABEL_FONT = "LabelFont" -// const val VALUE_LINE_STYLE_SOLID = "LineStyleSolid"; -// const val VALUE_LINE_STYLE_DASHED = "LineStyleDashed"; -// const val VALUE_LINE_STYLE_DOTTED = "LineStyleDotted"; } } \ No newline at end of file diff --git a/worldwind/src/commonMain/kotlin/earth/worldwind/layer/graticule/GraticuleSupport.kt b/worldwind/src/commonMain/kotlin/earth/worldwind/layer/graticule/GraticuleSupport.kt index 91a8323ff..8b50ca989 100644 --- a/worldwind/src/commonMain/kotlin/earth/worldwind/layer/graticule/GraticuleSupport.kt +++ b/worldwind/src/commonMain/kotlin/earth/worldwind/layer/graticule/GraticuleSupport.kt @@ -5,8 +5,10 @@ import earth.worldwind.layer.graticule.GraticuleRenderingParams.Companion.KEY_DR import earth.worldwind.layer.graticule.GraticuleRenderingParams.Companion.KEY_LABEL_COLOR import earth.worldwind.layer.graticule.GraticuleRenderingParams.Companion.KEY_LABEL_FONT import earth.worldwind.layer.graticule.GraticuleRenderingParams.Companion.KEY_LINE_COLOR +import earth.worldwind.layer.graticule.GraticuleRenderingParams.Companion.KEY_LINE_STYLE import earth.worldwind.layer.graticule.GraticuleRenderingParams.Companion.KEY_LINE_WIDTH import earth.worldwind.render.* +import earth.worldwind.render.image.ImageSource import earth.worldwind.shape.Label import earth.worldwind.shape.Path import earth.worldwind.shape.ShapeAttributes @@ -60,7 +62,7 @@ internal class GraticuleSupport { if (params[KEY_DRAW_LINES] == null) params[KEY_DRAW_LINES] = true if (params[KEY_LINE_COLOR] == null) params[KEY_LINE_COLOR] = Color(255, 255, 255) // White if (params[KEY_LINE_WIDTH] == null) params[KEY_LINE_WIDTH] = .5f -// if (params[KEY_LINE_STYLE] == null) params[KEY_LINE_STYLE] = GraticuleRenderingParams.VALUE_LINE_STYLE_SOLID + if (params[KEY_LINE_STYLE] == null) params[KEY_LINE_STYLE] = LineStyle.SOLID if (params[KEY_DRAW_LABELS] == null) params[KEY_DRAW_LABELS] = true if (params[KEY_LABEL_COLOR] == null) params[KEY_LABEL_COLOR] = Color(255, 255, 255) // White if (params[KEY_LABEL_FONT] == null) params[KEY_LABEL_FONT] = Font("arial", FontWeight.BOLD, 12) @@ -98,27 +100,14 @@ internal class GraticuleSupport { attrs.isDrawOutline = true // Apply "line" properties. - val o = params[KEY_LINE_COLOR] - if (o is Color) attrs.outlineColor = applyOpacity(o, opacity) - val lineWidth = params.getFloatValue(KEY_LINE_WIDTH) - if (lineWidth != null) attrs.outlineWidth = lineWidth -// val s = params.getStringValue(KEY_LINE_STYLE) -// when { -// VALUE_LINE_STYLE_SOLID.equals(s, true) -> { -// attrs.outlineStipplePattern = 0xAAAA.toShort() -// attrs.outlineStippleFactor = 0 -// } -// VALUE_LINE_STYLE_DASHED.equals(s, true) -> { -// val baseFactor = lineWidth?.roundToInt() ?: 1 -// attrs.outlineStipplePattern = 0xAAAA.toShort() -// attrs.outlineStippleFactor = 3 * baseFactor -// } -// VALUE_LINE_STYLE_DOTTED.equals(s, true) -> { -// val baseFactor = lineWidth?.roundToInt() ?: 1 -// attrs.outlineStipplePattern =0xAAAA.toShort() -// attrs.outlineStippleFactor = baseFactor -// } -// } + val color = params[KEY_LINE_COLOR] + if (color is Color) attrs.outlineColor = applyOpacity(color, opacity) + val lineWidth = params[KEY_LINE_WIDTH] + if (lineWidth is Float) attrs.outlineWidth = lineWidth + val style = params[KEY_LINE_STYLE] + if (style is LineStyle && style.factor > 0) { + attrs.outlineImageSource = ImageSource.fromLineStipple(style.factor, style.pattern) + } return attrs } diff --git a/worldwind/src/commonMain/kotlin/earth/worldwind/layer/graticule/LineStyle.kt b/worldwind/src/commonMain/kotlin/earth/worldwind/layer/graticule/LineStyle.kt new file mode 100644 index 000000000..e1ea7662c --- /dev/null +++ b/worldwind/src/commonMain/kotlin/earth/worldwind/layer/graticule/LineStyle.kt @@ -0,0 +1,20 @@ +package earth.worldwind.layer.graticule + +/** + * A line stipple pattern. + * + * @param factor specifies the number of times each bit in the pattern is repeated before the next bit is used. For + * example, if the factor is 3, each bit is repeated three times before using the next bit. The + * specified factor must be either zero or an integer greater than 0. A factor of 0 indicates no + * stippling. + * @param pattern specifies a number whose lower 16 bits define a pattern of which pixels in the image are white and + * which are transparent. Each bit corresponds to a pixel, and the pattern repeats after every n*16 + * pixels, where n is the factor. For example, if the factor is 3, each bit in the pattern is + * repeated three times before using the next bit. + */ +enum class LineStyle(val factor: Int, val pattern: Short) { + SOLID(0, 0x0000), + DASHED(1, 0xFFE0.toShort()), + DOTTED(2, 0xCCCC.toShort()), + DASH_DOTTED(2, 0xFFCC.toShort()) +} \ No newline at end of file