添加 SlopeResult 显示的开关
This commit is contained in:
@@ -11,6 +11,7 @@ import com.google.android.material.slider.RangeSlider
|
|||||||
import com.google.android.material.slider.Slider
|
import com.google.android.material.slider.Slider
|
||||||
import com.icegps.common.helper.GeoHelper
|
import com.icegps.common.helper.GeoHelper
|
||||||
import com.icegps.geotools.databinding.ActivityMainBinding
|
import com.icegps.geotools.databinding.ActivityMainBinding
|
||||||
|
import com.icegps.math.geometry.Angle
|
||||||
import com.icegps.math.geometry.degrees
|
import com.icegps.math.geometry.degrees
|
||||||
import com.icegps.shared.model.GeoPoint
|
import com.icegps.shared.model.GeoPoint
|
||||||
import com.mapbox.geojson.Point
|
import com.mapbox.geojson.Point
|
||||||
@@ -21,7 +22,10 @@ import kotlinx.coroutines.flow.MutableStateFlow
|
|||||||
import kotlinx.coroutines.flow.combine
|
import kotlinx.coroutines.flow.combine
|
||||||
import kotlinx.coroutines.flow.filterNotNull
|
import kotlinx.coroutines.flow.filterNotNull
|
||||||
import kotlinx.coroutines.flow.launchIn
|
import kotlinx.coroutines.flow.launchIn
|
||||||
|
import kotlinx.coroutines.flow.map
|
||||||
import kotlinx.coroutines.flow.onEach
|
import kotlinx.coroutines.flow.onEach
|
||||||
|
import kotlin.uuid.ExperimentalUuidApi
|
||||||
|
import kotlin.uuid.Uuid
|
||||||
|
|
||||||
class MainActivity : AppCompatActivity() {
|
class MainActivity : AppCompatActivity() {
|
||||||
private lateinit var binding: ActivityMainBinding
|
private lateinit var binding: ActivityMainBinding
|
||||||
@@ -166,11 +170,16 @@ class MainActivity : AppCompatActivity() {
|
|||||||
showDesignHeight.value = isChecked
|
showDesignHeight.value = isChecked
|
||||||
}
|
}
|
||||||
earthworkManager.setupOnMoveListener()
|
earthworkManager.setupOnMoveListener()
|
||||||
|
binding.switchSlopeResult.setOnCheckedChangeListener { _, isChecked ->
|
||||||
|
slopeResultVisible.value = isChecked
|
||||||
|
}
|
||||||
initData()
|
initData()
|
||||||
}
|
}
|
||||||
|
|
||||||
private val showDesignHeight = MutableStateFlow(false)
|
private val showDesignHeight = MutableStateFlow(false)
|
||||||
|
private val slopeResultVisible = MutableStateFlow(false)
|
||||||
|
|
||||||
|
@OptIn(ExperimentalUuidApi::class)
|
||||||
private fun initData() {
|
private fun initData() {
|
||||||
viewModel.points.onEach {
|
viewModel.points.onEach {
|
||||||
contoursManager.updatePoints(it)
|
contoursManager.updatePoints(it)
|
||||||
@@ -183,14 +192,32 @@ class MainActivity : AppCompatActivity() {
|
|||||||
earthworkManager.slopeDirection.onEach {
|
earthworkManager.slopeDirection.onEach {
|
||||||
binding.slopeDirection.value = it.degrees.toFloat()
|
binding.slopeDirection.value = it.degrees.toFloat()
|
||||||
}.launchIn(lifecycleScope)
|
}.launchIn(lifecycleScope)
|
||||||
|
val slopeResultSourceId: String = Uuid.random().toString()
|
||||||
|
val slopeResultLayerId: String = Uuid.random().toString()
|
||||||
combine(
|
combine(
|
||||||
earthworkManager.slopeDirection,
|
earthworkManager.slopeDirection,
|
||||||
earthworkManager.slopePercentage,
|
earthworkManager.slopePercentage,
|
||||||
earthworkManager.baseHeightOffset,
|
earthworkManager.baseHeightOffset,
|
||||||
contoursManager.gridModel,
|
contoursManager.gridModel,
|
||||||
showDesignHeight
|
showDesignHeight,
|
||||||
) { slopeDirection, slopePercentage, baseHeightOffset, gridModel, showDesignHeight ->
|
slopeResultVisible
|
||||||
gridModel?.let { gridModel ->
|
) {
|
||||||
|
Params6(
|
||||||
|
p1 = it[0] as Angle,
|
||||||
|
p2 = it[1] as Double,
|
||||||
|
p3 = it[2] as Double,
|
||||||
|
p4 = it[3] as? GridModel?,
|
||||||
|
p5 = it[4] as Boolean,
|
||||||
|
p6 = it[5] as Boolean
|
||||||
|
)
|
||||||
|
}.map { (slopeDirection, slopePercentage, baseHeightOffset, gridModel, showDesignHeight, slopeResultVisible) ->
|
||||||
|
if (!slopeResultVisible) {
|
||||||
|
mapView.mapboxMap.getStyle { style ->
|
||||||
|
style.removeStyleLayer(slopeResultLayerId)
|
||||||
|
style.removeStyleLayer("${slopeResultLayerId}-outline")
|
||||||
|
style.removeStyleSource(slopeResultSourceId)
|
||||||
|
}
|
||||||
|
} else gridModel?.let { gridModel ->
|
||||||
val slopeResult: SlopeResult = SlopeCalculator.calculateSlope(
|
val slopeResult: SlopeResult = SlopeCalculator.calculateSlope(
|
||||||
grid = gridModel,
|
grid = gridModel,
|
||||||
slopeDirection = slopeDirection.degrees,
|
slopeDirection = slopeDirection.degrees,
|
||||||
@@ -200,6 +227,8 @@ class MainActivity : AppCompatActivity() {
|
|||||||
mapView.displaySlopeResult(
|
mapView.displaySlopeResult(
|
||||||
originalGrid = gridModel,
|
originalGrid = gridModel,
|
||||||
slopeResult = slopeResult,
|
slopeResult = slopeResult,
|
||||||
|
sourceId = slopeResultSourceId,
|
||||||
|
layerId = slopeResultLayerId,
|
||||||
palette = contoursManager.simplePalette::palette,
|
palette = contoursManager.simplePalette::palette,
|
||||||
showDesignHeight = showDesignHeight
|
showDesignHeight = showDesignHeight
|
||||||
)
|
)
|
||||||
@@ -208,6 +237,22 @@ class MainActivity : AppCompatActivity() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
data class Params6<
|
||||||
|
out P1,
|
||||||
|
out P2,
|
||||||
|
out P3,
|
||||||
|
out P4,
|
||||||
|
out P5,
|
||||||
|
out P6,
|
||||||
|
>(
|
||||||
|
val p1: P1,
|
||||||
|
val p2: P2,
|
||||||
|
val p3: P3,
|
||||||
|
val p4: P4,
|
||||||
|
val p5: P5,
|
||||||
|
val p6: P6,
|
||||||
|
)
|
||||||
|
|
||||||
val home = GeoPoint(114.476060, 22.771073, 30.897)
|
val home = GeoPoint(114.476060, 22.771073, 30.897)
|
||||||
|
|
||||||
fun initGeoHelper(base: GeoPoint = home) {
|
fun initGeoHelper(base: GeoPoint = home) {
|
||||||
|
|||||||
@@ -1,123 +0,0 @@
|
|||||||
package com.icegps.geotools
|
|
||||||
|
|
||||||
import android.graphics.Color
|
|
||||||
import com.icegps.common.helper.GeoHelper
|
|
||||||
import com.icegps.math.geometry.Vector3D
|
|
||||||
import com.icegps.geotools.ktx.toMapboxPoint
|
|
||||||
import com.mapbox.geojson.Feature
|
|
||||||
import com.mapbox.geojson.FeatureCollection
|
|
||||||
import com.mapbox.geojson.LineString
|
|
||||||
import com.mapbox.geojson.Polygon
|
|
||||||
import com.mapbox.maps.MapView
|
|
||||||
import com.mapbox.maps.Style
|
|
||||||
import com.mapbox.maps.extension.style.layers.addLayer
|
|
||||||
import com.mapbox.maps.extension.style.layers.generated.fillLayer
|
|
||||||
import com.mapbox.maps.extension.style.layers.generated.lineLayer
|
|
||||||
import com.mapbox.maps.extension.style.layers.properties.generated.LineCap
|
|
||||||
import com.mapbox.maps.extension.style.layers.properties.generated.LineJoin
|
|
||||||
import com.mapbox.maps.extension.style.sources.addSource
|
|
||||||
import com.mapbox.maps.extension.style.sources.generated.geoJsonSource
|
|
||||||
|
|
||||||
class PolygonTest(
|
|
||||||
private val mapView: MapView
|
|
||||||
) {
|
|
||||||
private val geoHelper = GeoHelper.Companion.getSharedInstance()
|
|
||||||
|
|
||||||
private val contourSourceId = "contour-source-id-0"
|
|
||||||
private val contourLayerId = "contour-layer-id-0"
|
|
||||||
|
|
||||||
private val fillSourceId = "fill-source-id-0"
|
|
||||||
private val fillLayerId = "fill-layer-id-0"
|
|
||||||
|
|
||||||
fun update(
|
|
||||||
outer: List<Vector3D>,
|
|
||||||
inner: List<Vector3D>,
|
|
||||||
other: List<Vector3D>
|
|
||||||
) {
|
|
||||||
val lineFeatures = mutableListOf<Feature>()
|
|
||||||
val fillFeatures = mutableListOf<Feature>()
|
|
||||||
|
|
||||||
val outerPoints = outer.map { it.toMapboxPoint() }
|
|
||||||
val innerPoints = inner.map { it.toMapboxPoint() }
|
|
||||||
val otherPoints = other.map { it.toMapboxPoint() }
|
|
||||||
val outerLine = LineString.fromLngLats(outerPoints)
|
|
||||||
Feature.fromGeometry(outerLine).also {
|
|
||||||
lineFeatures.add(it)
|
|
||||||
}
|
|
||||||
val innerLine = LineString.fromLngLats(innerPoints)
|
|
||||||
Feature.fromGeometry(innerLine).also {
|
|
||||||
lineFeatures.add(it)
|
|
||||||
}
|
|
||||||
Feature.fromGeometry(LineString.fromLngLats(otherPoints)).also {
|
|
||||||
lineFeatures.add(it)
|
|
||||||
}
|
|
||||||
|
|
||||||
//val polygon = Polygon.fromOuterInner(outerLine, innerLine)
|
|
||||||
val polygon = Polygon.fromLngLats(listOf(outerPoints, otherPoints, innerPoints))
|
|
||||||
|
|
||||||
mapView.mapboxMap.getStyle { style ->
|
|
||||||
if (false) setupLineLayer(
|
|
||||||
style = style,
|
|
||||||
sourceId = contourSourceId,
|
|
||||||
layerId = contourLayerId,
|
|
||||||
features = lineFeatures
|
|
||||||
)
|
|
||||||
setupFillLayer(
|
|
||||||
style = style,
|
|
||||||
sourceId = fillSourceId,
|
|
||||||
layerId = fillLayerId,
|
|
||||||
features = listOf(Feature.fromGeometry(polygon))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun setupLineLayer(
|
|
||||||
style: Style,
|
|
||||||
sourceId: String,
|
|
||||||
layerId: String,
|
|
||||||
features: List<Feature>
|
|
||||||
) {
|
|
||||||
style.removeStyleLayer(layerId)
|
|
||||||
style.removeStyleSource(sourceId)
|
|
||||||
|
|
||||||
val source = geoJsonSource(sourceId) {
|
|
||||||
featureCollection(FeatureCollection.fromFeatures(features))
|
|
||||||
}
|
|
||||||
style.addSource(source)
|
|
||||||
|
|
||||||
val layer = lineLayer(layerId, sourceId) {
|
|
||||||
lineColor(Color.RED)
|
|
||||||
lineWidth(2.0)
|
|
||||||
lineCap(LineCap.Companion.ROUND)
|
|
||||||
lineJoin(LineJoin.Companion.ROUND)
|
|
||||||
lineOpacity(0.8)
|
|
||||||
}
|
|
||||||
style.addLayer(layer)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun setupFillLayer(
|
|
||||||
style: Style,
|
|
||||||
sourceId: String,
|
|
||||||
layerId: String,
|
|
||||||
features: List<Feature>
|
|
||||||
) {
|
|
||||||
style.removeStyleLayer(layerId)
|
|
||||||
style.removeStyleSource(sourceId)
|
|
||||||
|
|
||||||
val source = geoJsonSource(sourceId) {
|
|
||||||
featureCollection(FeatureCollection.fromFeatures(features))
|
|
||||||
}
|
|
||||||
style.addSource(source)
|
|
||||||
|
|
||||||
val layer = fillLayer(fillLayerId, fillSourceId) {
|
|
||||||
fillColor(Color.YELLOW)
|
|
||||||
fillOpacity(0.3)
|
|
||||||
fillAntialias(true)
|
|
||||||
}
|
|
||||||
style.addLayer(layer)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun clear() {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -178,6 +178,13 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:switchPadding="16dp"
|
android:switchPadding="16dp"
|
||||||
android:text="显示设计面" />
|
android:text="显示设计面" />
|
||||||
|
|
||||||
|
<Switch
|
||||||
|
android:id="@+id/switch_slope_result"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:switchPadding="16dp"
|
||||||
|
android:text="显示计算的坡面" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
@@ -169,6 +169,13 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:switchPadding="16dp"
|
android:switchPadding="16dp"
|
||||||
android:text="显示设计面" />
|
android:text="显示设计面" />
|
||||||
|
|
||||||
|
<Switch
|
||||||
|
android:id="@+id/switch_slope_result"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:switchPadding="16dp"
|
||||||
|
android:text="显示计算的坡面" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<com.mapbox.maps.MapView
|
<com.mapbox.maps.MapView
|
||||||
|
|||||||
@@ -0,0 +1,71 @@
|
|||||||
|
package com.icegps.geotools
|
||||||
|
|
||||||
|
import com.icegps.geotools.ktx.area
|
||||||
|
import com.icegps.geotools.ktx.niceStr
|
||||||
|
import com.icegps.math.geometry.Vector3D
|
||||||
|
import com.icegps.triangulation.delaunayTriangulation
|
||||||
|
import org.junit.Test
|
||||||
|
import kotlin.math.max
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author tabidachinokaze
|
||||||
|
* @date 2025/11/26
|
||||||
|
*/
|
||||||
|
class TriangulationToGridTest {
|
||||||
|
@Test
|
||||||
|
fun testTriangulationToGrid() {
|
||||||
|
val points = listOf(
|
||||||
|
Vector3D(-10.0, 10.0, 0.0),
|
||||||
|
Vector3D(10.0, 10.0, 10.0),
|
||||||
|
Vector3D(-10.0, -10.0, 20.0),
|
||||||
|
Vector3D(10.0, -10.0, 30.0),
|
||||||
|
)
|
||||||
|
points.map {
|
||||||
|
it / 8
|
||||||
|
}.niceStr().let(::println)
|
||||||
|
val area = points.area
|
||||||
|
val cellSize = max(area.x + area.width, area.y + area.height) / 10
|
||||||
|
val triangulation = points.delaunayTriangulation()
|
||||||
|
val triangles = triangulation.triangles()
|
||||||
|
val grid = triangulationToGrid(
|
||||||
|
delaunator = triangulation,
|
||||||
|
cellSize = cellSize,
|
||||||
|
)
|
||||||
|
grid.string().let(::println)
|
||||||
|
val slopeResult = SlopeCalculator.calculateSlope(
|
||||||
|
grid = grid,
|
||||||
|
slopeDirection = 0.0,
|
||||||
|
slopePercentage = 100.0,
|
||||||
|
baseHeightOffset = 0.0
|
||||||
|
)
|
||||||
|
slopeResult.designSurface.string().let(::println)
|
||||||
|
println("原来的 Volume: ${grid.volumeSum()}")
|
||||||
|
println("做坡的 Volume: ${slopeResult.designSurface.volumeSum()}")
|
||||||
|
println(slopeResult.earthworkResult)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun GridModel.string() = buildString {
|
||||||
|
for (r in 0 until rows) {
|
||||||
|
for (c in 0 until cols) {
|
||||||
|
val originalElev = getValue(r, c) ?: continue
|
||||||
|
append("${originalElev.format()}, ")
|
||||||
|
}
|
||||||
|
appendLine()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun GridModel.volumeSum(): Double {
|
||||||
|
var volume = 0.0
|
||||||
|
for (r in 0 until rows) {
|
||||||
|
for (c in 0 until cols) {
|
||||||
|
val height = getValue(r, c) ?: continue
|
||||||
|
volume += height
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return volume
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Double.format(): String {
|
||||||
|
return "%.1f".format(this)
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user