forked from streetcomplete/StreetComplete
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
add quest to make surface=paved/unpaved roads more detailed
road part of streetcomplete#279
- Loading branch information
1 parent
a97cd9e
commit 5c47912
Showing
6 changed files
with
258 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
106 changes: 106 additions & 0 deletions
106
app/src/main/java/de/westnordost/streetcomplete/quests/surface/DetailRoadSurface.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
package de.westnordost.streetcomplete.quests.surface | ||
|
||
import android.util.Log | ||
import de.westnordost.osmapi.map.data.BoundingBox | ||
import de.westnordost.osmapi.map.data.Element | ||
import de.westnordost.streetcomplete.R | ||
import de.westnordost.streetcomplete.data.osm.changes.StringMapChangesBuilder | ||
import de.westnordost.streetcomplete.data.osm.elementgeometry.ElementGeometry | ||
import de.westnordost.streetcomplete.data.osm.mapdata.OverpassMapDataAndGeometryApi | ||
import de.westnordost.streetcomplete.data.osm.osmquest.OsmElementQuestType | ||
import de.westnordost.streetcomplete.data.osm.osmquest.SimpleOverpassQuestType | ||
import de.westnordost.streetcomplete.data.quest.QuestStatus | ||
import de.westnordost.streetcomplete.data.tagfilters.FiltersParser | ||
import de.westnordost.streetcomplete.data.tagfilters.getQuestPrintStatement | ||
import de.westnordost.streetcomplete.data.tagfilters.toGlobalOverpassBBox | ||
import de.westnordost.streetcomplete.quests.address.PlaceName | ||
import de.westnordost.streetcomplete.quests.address.StreetName | ||
import de.westnordost.streetcomplete.quests.localized_name.AddRoadName | ||
|
||
|
||
class DetailRoadSurface(private val overpassMapDataApi: OverpassMapDataAndGeometryApi) : OsmElementQuestType<DetailSurfaceAnswer> { | ||
override val commitMessage = "Add more detailed surfaces" | ||
override val wikiLink = "Key:surface" | ||
override val icon = R.drawable.ic_quest_street_surface_paved_detail // TODO: consider changing icon name or restricting to surface=paved | ||
|
||
override fun getTitle(tags: Map<String, String>): Int { | ||
val hasName = tags.containsKey("name") | ||
val isSquare = tags["area"] == "yes" | ||
|
||
return if (hasName) { | ||
if (isSquare) | ||
R.string.quest_streetSurface_square_name_title | ||
else | ||
R.string.quest_streetSurface_name_title | ||
} else { | ||
if (isSquare) | ||
R.string.quest_streetSurface_square_title | ||
else | ||
R.string.quest_streetSurface_title | ||
} | ||
} | ||
|
||
override fun createForm() = DetailRoadSurfaceForm() | ||
|
||
override fun download(bbox: BoundingBox, handler: (element: Element, geometry: ElementGeometry?) -> Unit): Boolean { | ||
return overpassMapDataApi.query(getOverpassQuery(bbox), handler) | ||
} | ||
|
||
override fun isApplicableTo(element: Element): Boolean? { | ||
if(!REQUIRED_MINIMAL_MATCH_TFE.matches(element)) { | ||
return false; | ||
} | ||
element.tags.forEach { | ||
if(it.key.contains("surface:")) { | ||
return false; | ||
} | ||
if(it.key.contains(":surface")) { | ||
return false; | ||
} | ||
} | ||
return true; | ||
} | ||
|
||
private fun getOverpassQuery(bbox: BoundingBox) = | ||
bbox.toGlobalOverpassBBox() + "\n" + """ | ||
way[surface~"^(${UNDETAILED_SURFACE_TAG_MATCH})${'$'}"][segregated!="yes"][highway ~ "^${ HIGHWAY_TAG_MATCH }${'$'}"] -> .surface_without_detail; | ||
// https://taginfo.openstreetmap.org//search?q=%3Asurface | ||
// https://taginfo.openstreetmap.org//search?q=surface: | ||
way[~"(:surface|surface:)"~"."] -> .extra_tags; | ||
(.surface_without_detail; - .extra_tags;); | ||
""".trimIndent() + "\n" + | ||
getQuestPrintStatement() | ||
|
||
private val HIGHWAY_TAG_MATCH = ROADS_WITH_SURFACES_BROADLY_DEFINED.joinToString("|") | ||
private val UNDETAILED_SURFACE_TAG_MATCH = "paved|unpaved" | ||
private val REQUIRED_MINIMAL_MATCH_TFE by lazy { FiltersParser().parse( | ||
"ways with surface ~ ${UNDETAILED_SURFACE_TAG_MATCH} and segregated!=yes and highway ~ ${HIGHWAY_TAG_MATCH}" | ||
)} | ||
|
||
override val isSplitWayEnabled = true | ||
|
||
override fun applyAnswerTo(answer: DetailSurfaceAnswer, changes: StringMapChangesBuilder) { | ||
when(answer) { | ||
is SurfaceAnswer -> { | ||
changes.modify("surface", answer.value) | ||
} | ||
is DetailingImpossibleAnswer -> { | ||
changes.add("surface:note", answer.value) | ||
} | ||
} | ||
} | ||
|
||
companion object { | ||
// well, all roads have surfaces, what I mean is that not all ways with highway key are | ||
// "something with a surface" | ||
// see https://github.com/westnordost/StreetComplete/pull/327#discussion_r121937808 | ||
private val ROADS_WITH_SURFACES_BROADLY_DEFINED = arrayOf( | ||
"trunk","trunk_link","motorway","motorway_link", | ||
"primary", "primary_link", "secondary", "secondary_link", "tertiary", "tertiary_link", | ||
"unclassified", "residential", "living_street", "pedestrian", "track", "road", | ||
"service" | ||
) | ||
} | ||
} |
113 changes: 113 additions & 0 deletions
113
app/src/main/java/de/westnordost/streetcomplete/quests/surface/DetailRoadSurfaceForm.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,113 @@ | ||
package de.westnordost.streetcomplete.quests.surface | ||
|
||
import android.os.Bundle | ||
import android.view.LayoutInflater | ||
import android.view.View | ||
import android.view.ViewGroup | ||
import android.widget.EditText | ||
import androidx.appcompat.app.AlertDialog | ||
import de.westnordost.streetcomplete.R | ||
import de.westnordost.streetcomplete.quests.AGroupedImageListQuestAnswerFragment | ||
import de.westnordost.streetcomplete.quests.OtherAnswer | ||
import de.westnordost.streetcomplete.util.TextChangedWatcher | ||
import de.westnordost.streetcomplete.view.Item | ||
|
||
class DetailRoadSurfaceForm : AGroupedImageListQuestAnswerFragment<String, DetailSurfaceAnswer>() { | ||
|
||
override val topItems get() = | ||
if (osmElement!!.tags["surface"] == "paved") | ||
listOf(Surface.ASPHALT, Surface.CONCRETE, Surface.SETT, Surface.PAVING_STONES, Surface.WOOD, Surface.GRASS_PAVER).toItems() | ||
else | ||
listOf(Surface.DIRT, Surface.GRASS, Surface.PEBBLES, Surface.FINE_GRAVEL, Surface.SAND, Surface.COMPACTED).toItems() | ||
|
||
// note that for unspecific groups null is used as a value, it makes them unselecteable | ||
override val allItems = listOf( | ||
Item(null, R.drawable.panorama_surface_paved, R.string.quest_surface_value_paved, null, listOf( | ||
Surface.ASPHALT, Surface.CONCRETE, Surface.PAVING_STONES, | ||
Surface.SETT, Surface.UNHEWN_COBBLESTONE, Surface.GRASS_PAVER, | ||
Surface.WOOD, Surface.METAL | ||
).toItems()), | ||
Item(null, R.drawable.panorama_surface_unpaved, R.string.quest_surface_value_unpaved, null, listOf( | ||
Surface.COMPACTED, Surface.FINE_GRAVEL, Surface.GRAVEL, | ||
Surface.PEBBLES | ||
).toItems()), | ||
Item(null, R.drawable.panorama_surface_ground, R.string.quest_surface_value_ground, null, listOf( | ||
Surface.DIRT, Surface.GRASS, Surface.SAND | ||
).toItems()) | ||
) | ||
|
||
private var isInExplanationMode = false; | ||
private var explanationInput: EditText? = null | ||
|
||
override val otherAnswers = listOf( | ||
OtherAnswer(R.string.ic_quest_surface_detailed_answer_impossible) { confirmSwitchToNoDetailedTagPossible() } | ||
) | ||
|
||
private fun setLayout(layoutResourceId: Int) { | ||
val view = setContentView(layoutResourceId) | ||
|
||
val onChanged = TextChangedWatcher { | ||
checkIsFormComplete() | ||
} | ||
explanationInput = view.findViewById(R.id.explanationInput) | ||
explanationInput?.addTextChangedListener(onChanged) | ||
} | ||
|
||
private val explanation: String get() = explanationInput?.text?.toString().orEmpty().trim() | ||
|
||
override fun isFormComplete(): Boolean { | ||
if(isInExplanationMode) { | ||
return explanation.isNotEmpty() | ||
} else { | ||
return super.isFormComplete() | ||
} | ||
} | ||
|
||
override fun onClickOk() { | ||
if(isInExplanationMode) { | ||
applyAnswer(DetailingImpossibleAnswer(explanation)) | ||
} else { | ||
super.onClickOk(); | ||
} | ||
} | ||
|
||
override fun onClickOk(value: String) { | ||
// must not happen in isInExplanationMode | ||
applyAnswer(SurfaceAnswer(value)) | ||
} | ||
|
||
private fun confirmSwitchToNoDetailedTagPossible() { | ||
AlertDialog.Builder(activity!!) | ||
.setMessage(R.string.ic_quest_surface_detailed_answer_impossible_confirmation) | ||
.setPositiveButton(R.string.quest_generic_confirmation_yes) { | ||
_, _ -> switchToExplanationLayout() | ||
} | ||
.setNegativeButton(R.string.quest_generic_cancel, null) | ||
.show() | ||
|
||
} | ||
|
||
private fun switchToExplanationLayout(){ | ||
isInExplanationMode = true | ||
setLayout(R.layout.quest_surface_detailed_answer_impossible) | ||
} | ||
|
||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { | ||
val view = super.onCreateView(inflater, container, savedInstanceState) | ||
|
||
isInExplanationMode = savedInstanceState?.getBoolean(IS_IN_EXPLANATION_MODE) ?: false | ||
setLayout(if (isInExplanationMode) R.layout.quest_surface_detailed_answer_impossible else R.layout.quest_generic_list) | ||
|
||
return view | ||
} | ||
|
||
override fun onSaveInstanceState(outState: Bundle) { | ||
super.onSaveInstanceState(outState) | ||
outState.putBoolean(IS_IN_EXPLANATION_MODE, isInExplanationMode) | ||
} | ||
|
||
companion object { | ||
private const val IS_IN_EXPLANATION_MODE = "is_in_explanation_mode" | ||
} | ||
|
||
} |
6 changes: 6 additions & 0 deletions
6
app/src/main/java/de/westnordost/streetcomplete/quests/surface/DetailSurfaceAnswer.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
package de.westnordost.streetcomplete.quests.surface | ||
|
||
sealed class DetailSurfaceAnswer(open val value: String) | ||
|
||
data class DetailingImpossibleAnswer(override val value: String) : DetailSurfaceAnswer(value) | ||
data class SurfaceAnswer(override val value: String) : DetailSurfaceAnswer(value) |
27 changes: 27 additions & 0 deletions
27
app/src/main/res/layout/quest_surface_detailed_answer_impossible.xml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
<?xml version="1.0" encoding="utf-8"?> | ||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" | ||
xmlns:tools="http://schemas.android.com/tools" | ||
android:orientation="vertical" | ||
android:layout_width="match_parent" | ||
android:layout_height="wrap_content" | ||
android:divider="@drawable/space_8dp" | ||
android:showDividers="middle"> | ||
|
||
<TextView | ||
android:layout_width="wrap_content" | ||
android:layout_height="wrap_content" | ||
android:text="@string/ic_quest_surface_detailed_answer_impossible_description"/> | ||
|
||
<EditText | ||
android:id="@+id/explanationInput" | ||
android:layout_width="match_parent" | ||
android:layout_height="wrap_content" | ||
android:inputType="textMultiLine" | ||
android:gravity="top|start" | ||
android:minLines="3" | ||
tools:text="Surface is a mosaic of paving stones, wood and metal." | ||
android:maxLength="255" | ||
android:scrollHorizontally="false" | ||
android:autofillHints="Tap here to write." | ||
/> | ||
</LinearLayout> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters