diff --git a/app/src/androidMain/kotlin/de/westnordost/streetcomplete/quests/QuestsModule.kt b/app/src/androidMain/kotlin/de/westnordost/streetcomplete/quests/QuestsModule.kt index c8e40c8db76..428ae0c1cbc 100644 --- a/app/src/androidMain/kotlin/de/westnordost/streetcomplete/quests/QuestsModule.kt +++ b/app/src/androidMain/kotlin/de/westnordost/streetcomplete/quests/QuestsModule.kt @@ -120,6 +120,7 @@ import de.westnordost.streetcomplete.quests.internet_access.AddInternetAccess import de.westnordost.streetcomplete.quests.kerb_height.AddKerbHeight import de.westnordost.streetcomplete.quests.lamp_type.AddLampType import de.westnordost.streetcomplete.quests.lamp_mount.AddLampMount +import de.westnordost.streetcomplete.quests.direction.AddDirection import de.westnordost.streetcomplete.quests.lanes.AddLanes import de.westnordost.streetcomplete.quests.leaf_detail.AddForestLeafType import de.westnordost.streetcomplete.quests.leaf_detail.AddTreeLeafType @@ -656,6 +657,7 @@ fun getQuestTypeList( EE_QUEST_OFFSET + 54 to AddLampType(), EE_QUEST_OFFSET + 55 to AddPostOfficeType(), EE_QUEST_OFFSET + 57 to AddLampMount(), + EE_QUEST_OFFSET + 58 to AddDirection(), EE_QUEST_OFFSET + 10 to OsmoseQuest(osmoseDao), EE_QUEST_OFFSET + 11 to CustomQuest(customQuestList), // POI quests diff --git a/app/src/androidMain/kotlin/de/westnordost/streetcomplete/quests/direction/AddDirection.kt b/app/src/androidMain/kotlin/de/westnordost/streetcomplete/quests/direction/AddDirection.kt new file mode 100644 index 00000000000..19f0c9bb135 --- /dev/null +++ b/app/src/androidMain/kotlin/de/westnordost/streetcomplete/quests/direction/AddDirection.kt @@ -0,0 +1,32 @@ +package de.westnordost.streetcomplete.quests.direction + +import de.westnordost.streetcomplete.R +import de.westnordost.streetcomplete.data.osm.geometry.ElementGeometry +import de.westnordost.streetcomplete.data.osm.osmquests.OsmFilterQuestType +import de.westnordost.streetcomplete.osm.Tags + +class AddDirection : OsmFilterQuestType() { + + override val elementFilter = """ + nodes with ( + amenity = bench + or advertising = billboard + or highway = traffic_sign + or amenity = vending_machine + or traffic_sign + ) + and !direction + """ + override val changesetComment = "Specify direction object is facing" + override val wikiLink = "Key:direction" + override val icon = R.drawable.ic_quest_direction + override val defaultDisabledMessage: Int = R.string.default_disabled_msg_ee + + override fun getTitle(tags: Map) = R.string.quest_direction_title + + override fun createForm() = AddDirectionForm() + + override fun applyAnswerTo(answer: Int, tags: Tags, geometry: ElementGeometry, timestampEdited: Long) { + tags["direction"] = answer.toString() + } +} diff --git a/app/src/androidMain/kotlin/de/westnordost/streetcomplete/quests/direction/AddDirectionForm.kt b/app/src/androidMain/kotlin/de/westnordost/streetcomplete/quests/direction/AddDirectionForm.kt new file mode 100644 index 00000000000..75d600339ee --- /dev/null +++ b/app/src/androidMain/kotlin/de/westnordost/streetcomplete/quests/direction/AddDirectionForm.kt @@ -0,0 +1,49 @@ +package de.westnordost.streetcomplete.quests.direction + +import android.os.Bundle +import android.view.View +import androidx.compose.material.Surface +import androidx.compose.runtime.MutableState +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.saveable.rememberSaveable +import de.westnordost.streetcomplete.databinding.ComposeViewBinding +import de.westnordost.streetcomplete.quests.AbstractOsmQuestForm +import de.westnordost.streetcomplete.screens.main.bottom_sheet.IsMapOrientationAware +import de.westnordost.streetcomplete.ui.util.content +import de.westnordost.streetcomplete.util.math.normalizeDegrees + +class AddDirectionForm : AbstractOsmQuestForm(), IsMapOrientationAware { + + override val contentLayoutResId = de.westnordost.streetcomplete.R.layout.compose_view + private val binding by contentViewBinding(ComposeViewBinding::bind) + + private lateinit var direction: MutableState + private lateinit var mapRotation: MutableState + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + binding.composeViewBase.content { + direction = rememberSaveable { mutableStateOf(0) } + mapRotation = remember { mutableStateOf(0f) } + Surface { + DirectionPicker( + mapRotation = mapRotation.value, + angle = mapRotation.value + direction.value, + onAngleChange = { direction.value = (it - mapRotation.value).toInt() }, + ) + } + } + checkIsFormComplete() + } + + override fun onMapOrientation(rotation: Double, tilt: Double) { + mapRotation.value = -rotation.toFloat() + } + + override fun onClickOk() { + applyAnswer(normalizeDegrees(direction.value.toFloat()).toInt()) + } + + override fun isFormComplete() = true +} diff --git a/app/src/androidMain/kotlin/de/westnordost/streetcomplete/quests/direction/DirectionPicker.kt b/app/src/androidMain/kotlin/de/westnordost/streetcomplete/quests/direction/DirectionPicker.kt new file mode 100644 index 00000000000..03343ceb3ed --- /dev/null +++ b/app/src/androidMain/kotlin/de/westnordost/streetcomplete/quests/direction/DirectionPicker.kt @@ -0,0 +1,87 @@ +package de.westnordost.streetcomplete.quests.direction + +import androidx.compose.foundation.Canvas +import androidx.compose.foundation.gestures.detectDragGestures +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.size +import androidx.compose.material.MaterialTheme +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.graphics.graphicsLayer +import androidx.compose.ui.input.pointer.pointerInput +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.dp +import de.westnordost.streetcomplete.R +import de.westnordost.streetcomplete.util.math.normalizeDegrees +import kotlin.math.atan2 +import kotlin.math.cos +import kotlin.math.roundToInt +import kotlin.math.sin + +@Composable +fun DirectionPicker( + mapRotation: Float, + angle: Float, + onAngleChange: (Int) -> Unit, + modifier: Modifier = Modifier, + diameter: Dp = 200.dp +) { + Column(horizontalAlignment = Alignment.CenterHorizontally, modifier = modifier) { + Box( + modifier = Modifier + .size(diameter) + .graphicsLayer { rotationZ = mapRotation } + .pointerInput(mapRotation) { + detectDragGestures { change, _ -> + val center = Offset(size.width / 2f, size.height / 2f) + val dx = change.position.x - center.x + val dy = center.y - change.position.y + var deg = Math.toDegrees(atan2(dx.toDouble(), dy.toDouble())).toFloat() + deg = normalizeDegrees(deg + mapRotation) + val snapped = (deg / 5f).roundToInt() * 5 + onAngleChange(((snapped % 360) + 360) % 360) + } + } + ) { + val lineColor = MaterialTheme.colors.onSurface + Canvas(modifier = Modifier.fillMaxSize()) { + val r = size.minDimension / 2f + val center = Offset(size.width / 2f, size.height / 2f) + for (i in 0 until 360 step 5) { + val rad = Math.toRadians((i - 90).toDouble()) + val outer = Offset( + center.x + cos(rad).toFloat() * r, + center.y + sin(rad).toFloat() * r + ) + val innerR = if (i % 30 == 0) r * 0.75f else r * 0.85f + val inner = Offset( + center.x + cos(rad).toFloat() * innerR, + center.y + sin(rad).toFloat() * innerR + ) + drawLine( + color = lineColor, + start = inner, + end = outer, + strokeWidth = if (i % 30 == 0) 3f else 1f + ) + } + } + androidx.compose.foundation.Image( + painter = painterResource(R.drawable.ic_direction_pointer_48dp), + contentDescription = null, + modifier = Modifier + .align(Alignment.Center) + .size(diameter * 0.6f) + .graphicsLayer { rotationZ = angle.toFloat() - mapRotation } + ) + } + Text(text = "${normalizeDegrees(angle.toFloat() - mapRotation).toInt()}°") + } +} + diff --git a/app/src/androidMain/res/values/strings_ee.xml b/app/src/androidMain/res/values/strings_ee.xml index 9aa2015aad8..1dfa40027d4 100644 --- a/app/src/androidMain/res/values/strings_ee.xml +++ b/app/src/androidMain/res/values/strings_ee.xml @@ -804,4 +804,7 @@ Out of the box SCEE is configured to behave very similar to StreetComplete.Bamboo Adobe + + Which direction is this facing? + diff --git a/app/src/main/res/drawable/ic_direction_pointer_48dp.xml b/app/src/main/res/drawable/ic_direction_pointer_48dp.xml new file mode 100644 index 00000000000..7b4b250b2fd --- /dev/null +++ b/app/src/main/res/drawable/ic_direction_pointer_48dp.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_quest_direction.xml b/app/src/main/res/drawable/ic_quest_direction.xml new file mode 100644 index 00000000000..48efbae6e7a --- /dev/null +++ b/app/src/main/res/drawable/ic_quest_direction.xml @@ -0,0 +1,16 @@ + + + + + diff --git a/res/graphics/direction/direction_pointer.svg b/res/graphics/direction/direction_pointer.svg new file mode 100644 index 00000000000..3d7cf33a557 --- /dev/null +++ b/res/graphics/direction/direction_pointer.svg @@ -0,0 +1,112 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/res/graphics/quest/compass_needle.svg b/res/graphics/quest/compass_needle.svg new file mode 100644 index 00000000000..7cd452266ff --- /dev/null +++ b/res/graphics/quest/compass_needle.svg @@ -0,0 +1,67 @@ + + + + + + image/svg+xml + + + + + + + + + + + + +