Skip to content

Commit d3ea1fa

Browse files
committed
try to generify lookup of nearby elements to display
1 parent 9365a94 commit d3ea1fa

File tree

6 files changed

+126
-38
lines changed

6 files changed

+126
-38
lines changed

app/src/androidMain/kotlin/de/westnordost/streetcomplete/data/quest/atp/CreatePoiBasedOnAtp.kt

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package de.westnordost.streetcomplete.data.quest.atp
22

3-
import de.westnordost.osmfeatures.Feature
43
import de.westnordost.streetcomplete.R
54
import de.westnordost.streetcomplete.data.osm.mapdata.Element
65
import de.westnordost.streetcomplete.data.osm.mapdata.MapDataWithGeometry
@@ -18,6 +17,6 @@ class CreatePoiBasedOnAtp() : OsmCreateElementQuestType<CreatePoiBasedOnAtpAnswe
1817
override val achievements = listOf(CITIZEN)
1918
override val changesetComment = "Create POI surveyed by mapper, hint about missing entry was based on AllThePlaces data"
2019

21-
override fun getHighlightedElements(element: Element, getMapData: () -> MapDataWithGeometry) =
20+
override fun getHighlightedElements(element: Element?, getMapData: () -> MapDataWithGeometry) =
2221
getMapData().asSequence().filter { it.isPlaceOrDisusedPlace() }
2322
}

app/src/androidMain/kotlin/de/westnordost/streetcomplete/screens/main/MainActivity.kt

Lines changed: 105 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,6 @@ import de.westnordost.streetcomplete.data.osm.mapdata.Node
5656
import de.westnordost.streetcomplete.data.osm.mapdata.Way
5757
import de.westnordost.streetcomplete.data.osm.osmquests.OsmQuest
5858
import de.westnordost.streetcomplete.data.osmnotes.edits.NotesWithEditsSource
59-
import de.westnordost.streetcomplete.data.osmnotes.notequests.OsmNoteQuestAndroid
6059
import de.westnordost.streetcomplete.data.osmnotes.notequests.createOsmNoteQuest
6160
import de.westnordost.streetcomplete.data.osmtracks.Trackpoint
6261
import de.westnordost.streetcomplete.data.overlays.AndroidOverlay
@@ -115,7 +114,6 @@ import de.westnordost.streetcomplete.util.ktx.truncateTo6Decimals
115114
import de.westnordost.streetcomplete.util.location.FineLocationManager
116115
import de.westnordost.streetcomplete.util.location.LocationAvailabilityReceiver
117116
import de.westnordost.streetcomplete.util.location.LocationRequestFragment
118-
import de.westnordost.streetcomplete.util.logs.Log
119117
import de.westnordost.streetcomplete.util.math.area
120118
import de.westnordost.streetcomplete.util.math.enclosingBoundingBox
121119
import de.westnordost.streetcomplete.util.math.enlargedBy
@@ -189,11 +187,13 @@ class MainActivity :
189187
private var wasFollowingPosition: Boolean? = null
190188
private var wasNavigationMode: Boolean? = null
191189

192-
private val mapFragment: MainMapFragment? get() =
193-
supportFragmentManager.findFragmentById(R.id.mapFragment) as MainMapFragment?
190+
private val mapFragment: MainMapFragment?
191+
get() =
192+
supportFragmentManager.findFragmentById(R.id.mapFragment) as MainMapFragment?
194193

195-
private val bottomSheetFragment: Fragment? get() =
196-
supportFragmentManager.findFragmentByTag(BOTTOM_SHEET)
194+
private val bottomSheetFragment: Fragment?
195+
get() =
196+
supportFragmentManager.findFragmentByTag(BOTTOM_SHEET)
197197

198198
/* +++++++++++++++++++++++++++++++++++++++ CALLBACKS ++++++++++++++++++++++++++++++++++++++++ */
199199

@@ -203,13 +203,14 @@ class MainActivity :
203203
}
204204
}
205205

206-
private val requestLocationPermissionResultReceiver: BroadcastReceiver = object : BroadcastReceiver() {
207-
override fun onReceive(context: Context, intent: Intent) {
208-
if (!intent.getBooleanExtra(LocationRequestFragment.GRANTED, false)) {
209-
toast(R.string.no_gps_no_quests, Toast.LENGTH_LONG)
206+
private val requestLocationPermissionResultReceiver: BroadcastReceiver =
207+
object : BroadcastReceiver() {
208+
override fun onReceive(context: Context, intent: Intent) {
209+
if (!intent.getBooleanExtra(LocationRequestFragment.GRANTED, false)) {
210+
toast(R.string.no_gps_no_quests, Toast.LENGTH_LONG)
211+
}
210212
}
211213
}
212-
}
213214

214215
//region Lifecycle - Android Lifecycle Callbacks
215216

@@ -459,14 +460,28 @@ class MainActivity :
459460
closeBottomSheet()
460461
}
461462

462-
override fun onComposeNote(editType: ElementEditType, element: Element, geometry: ElementGeometry, leaveNoteContext: String) {
463+
override fun onComposeNote(
464+
editType: ElementEditType,
465+
element: Element,
466+
geometry: ElementGeometry,
467+
leaveNoteContext: String,
468+
) {
463469
showInBottomSheet(
464-
LeaveNoteInsteadFragment.create(element.type, element.id, leaveNoteContext, geometry.center),
470+
LeaveNoteInsteadFragment.create(
471+
element.type,
472+
element.id,
473+
leaveNoteContext,
474+
geometry.center
475+
),
465476
false
466477
)
467478
}
468479

469-
override fun onSplitWay(editType: ElementEditType, way: Way, geometry: ElementPolylinesGeometry) {
480+
override fun onSplitWay(
481+
editType: ElementEditType,
482+
way: Way,
483+
geometry: ElementPolylinesGeometry,
484+
) {
470485
val mapFragment = mapFragment ?: return
471486
showInBottomSheet(SplitWayFragment.create(editType, way, geometry))
472487
mapFragment.highlightGeometry(geometry)
@@ -483,7 +498,11 @@ class MainActivity :
483498

484499
/* ------------------------------- SplitWayFragment.Listener -------------------------------- */
485500

486-
override fun onSplittedWay(editType: ElementEditType, way: Way, geometry: ElementPolylinesGeometry) {
501+
override fun onSplittedWay(
502+
editType: ElementEditType,
503+
way: Way,
504+
geometry: ElementPolylinesGeometry,
505+
) {
487506
showQuestSolvedAnimation(editType.icon, geometry.center)
488507
closeBottomSheet()
489508
}
@@ -492,7 +511,10 @@ class MainActivity :
492511

493512
override fun onMoveNode(editType: ElementEditType, node: Node) {
494513
val mapFragment = mapFragment ?: return
495-
showInBottomSheet(MoveNodeFragment.create(editType, node), clearPreviousHighlighting = false)
514+
showInBottomSheet(
515+
MoveNodeFragment.create(editType, node),
516+
clearPreviousHighlighting = false
517+
)
496518
mapFragment.clearSelectedPins()
497519
mapFragment.hideNonHighlightedPins()
498520
if (editType !is Overlay) {
@@ -616,7 +638,12 @@ class MainActivity :
616638
val f = bottomSheetFragment
617639
if (f !is IsShowingElement) return@launch
618640
val elementKey = f.elementKey ?: return@launch
619-
val openElement = withContext(Dispatchers.IO) { mapDataWithEditsSource.get(elementKey.type, elementKey.id) }
641+
val openElement = withContext(Dispatchers.IO) {
642+
mapDataWithEditsSource.get(
643+
elementKey.type,
644+
elementKey.id
645+
)
646+
}
620647
// open element does not exist anymore after download
621648
if (openElement == null) {
622649
closeBottomSheet()
@@ -754,9 +781,11 @@ class MainActivity :
754781
!viewModel.locationState.value.isEnabled -> {
755782
requestLocation()
756783
}
784+
757785
!mapFragment.isFollowingPosition -> {
758786
setIsFollowingPosition(true)
759787
}
788+
760789
else -> {
761790
setIsNavigationMode(!mapFragment.isNavigationMode)
762791
}
@@ -793,7 +822,8 @@ class MainActivity :
793822
return null
794823
}
795824

796-
val enclosingBBox = displayArea.asBoundingBoxOfEnclosingTiles(ApplicationConstants.DOWNLOAD_TILE_ZOOM)
825+
val enclosingBBox =
826+
displayArea.asBoundingBoxOfEnclosingTiles(ApplicationConstants.DOWNLOAD_TILE_ZOOM)
797827
val areaInSqKm = enclosingBBox.area() / 1000000
798828
if (areaInSqKm > ApplicationConstants.MAX_DOWNLOADABLE_AREA_IN_SQKM) {
799829
toast(R.string.download_area_too_big, Toast.LENGTH_LONG)
@@ -833,8 +863,9 @@ class MainActivity :
833863
val uri = buildGeoUri(pos.latitude, pos.longitude, zoom)
834864

835865
val intent = Intent(Intent.ACTION_VIEW, uri.toUri())
836-
val otherMapAppInstalled = packageManager.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY)
837-
.any { !it.activityInfo.packageName.equals(packageName) }
866+
val otherMapAppInstalled =
867+
packageManager.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY)
868+
.any { !it.activityInfo.packageName.equals(packageName) }
838869
if (otherMapAppInstalled) {
839870
startActivity(intent)
840871
} else {
@@ -879,7 +910,10 @@ class MainActivity :
879910
private fun closeBottomSheet() {
880911
currentFocus?.hideKeyboard()
881912
if (bottomSheetFragment != null) {
882-
supportFragmentManager.popBackStack(BOTTOM_SHEET, FragmentManager.POP_BACK_STACK_INCLUSIVE)
913+
supportFragmentManager.popBackStack(
914+
BOTTOM_SHEET,
915+
FragmentManager.POP_BACK_STACK_INCLUSIVE
916+
)
883917
}
884918
clearHighlighting()
885919
unfreezeMap()
@@ -894,7 +928,10 @@ class MainActivity :
894928
freezeMap()
895929
if (bottomSheetFragment != null) {
896930
if (clearPreviousHighlighting) clearHighlighting()
897-
supportFragmentManager.popBackStack(BOTTOM_SHEET, FragmentManager.POP_BACK_STACK_INCLUSIVE)
931+
supportFragmentManager.popBackStack(
932+
BOTTOM_SHEET,
933+
FragmentManager.POP_BACK_STACK_INCLUSIVE
934+
)
898935
}
899936
val appearAnim = R.animator.quest_answer_form_appear
900937
val disappearAnim = R.animator.quest_answer_form_disappear
@@ -973,7 +1010,12 @@ class MainActivity :
9731010
return
9741011
}
9751012

976-
val element = withContext(Dispatchers.IO) { mapDataWithEditsSource.get(elementKey.type, elementKey.id) } ?: return
1013+
val element = withContext(Dispatchers.IO) {
1014+
mapDataWithEditsSource.get(
1015+
elementKey.type,
1016+
elementKey.id
1017+
)
1018+
} ?: return
9771019
val f = (overlay as? AndroidOverlay)?.createForm(element) ?: return
9781020
if (f.arguments == null) f.arguments = bundleOf()
9791021

@@ -1014,23 +1056,27 @@ class MainActivity :
10141056
val camera = mapFragment.cameraPosition
10151057
val rotation = camera?.rotation ?: 0.0
10161058
val tilt = camera?.tilt ?: 0.0
1017-
val args = AbstractQuestForm.createArguments(quest.key, quest.type, quest.geometry, rotation, tilt)
1059+
val args =
1060+
AbstractQuestForm.createArguments(quest.key, quest.type, quest.geometry, rotation, tilt)
10181061
f.requireArguments().putAll(args)
10191062

10201063
if (f is AbstractOsmQuestForm<*> && quest is OsmQuest) {
1021-
val element = withContext(Dispatchers.IO) { mapDataWithEditsSource.get(quest.elementType, quest.elementId) } ?: return
1064+
val element = withContext(Dispatchers.IO) {
1065+
mapDataWithEditsSource.get(
1066+
quest.elementType,
1067+
quest.elementId
1068+
)
1069+
} ?: return
10221070
val osmArgs = AbstractOsmQuestForm.createArguments(element)
10231071
f.requireArguments().putAll(osmArgs)
10241072
showHighlightedElements(quest, element)
10251073
}
10261074
if (f is AtpCreateForm && quest is CreateElementQuest) { // TODO fix ongoing confusion and mixing etween specific ATP creation quest and potentially wider class of OSMElementCreation quest - should I even try to support wider class of adder quests right now? Which parts are ATP specific and which are generic? Are more ATP quests viable?
1027-
val passingAtpArgs = AtpCreateForm.createArguments(quest.atpEntry) // use ATP equivalent of mapDataWithEditsSource like above?
1075+
val passingAtpArgs =
1076+
AtpCreateForm.createArguments(quest.atpEntry) // use ATP equivalent of mapDataWithEditsSource like above?
10281077
f.requireArguments().putAll(passingAtpArgs)
1078+
showHighlightedElementsAroundAtpEntryQuest(quest, quest.atpEntry)
10291079
}
1030-
1031-
// TODO: generalize showHighlightedElements or provide alternative one that does not get element feed into it - consult with Westnordost before refactoring it as it will be a bit obnoxious to implement
1032-
//showHighlightedElements(quest, element)
1033-
10341080
showInBottomSheet(f)
10351081

10361082
mapFragment.startFocus(quest.geometry, getQuestFormInsets())
@@ -1041,7 +1087,32 @@ class MainActivity :
10411087
}
10421088

10431089
private fun showHighlightedElements(quest: OsmQuest, element: Element) {
1044-
val bbox = quest.geometry.bounds.enlargedBy(quest.type.highlightedElementsRadius)
1090+
return showHighlightedElementsShared(
1091+
quest,
1092+
element.tags,
1093+
null,
1094+
quest.type.highlightedElementsRadius
1095+
)
1096+
}
1097+
1098+
private fun showHighlightedElementsAroundAtpEntryQuest(
1099+
quest: CreateElementQuest,
1100+
atpEntry: AtpEntry,
1101+
) {
1102+
// TODO is merge with showHighlightedElements a good idea?
1103+
// some challenges: it is not passing or having an element - changed that for nullable parameter - is it fine? Maybe effectively duplicating this function is nicer?
1104+
// passing highlightedElementsRadius is silly (maybe create interface used by both classes?)
1105+
val tags = atpEntry.tagsInATP
1106+
showHighlightedElementsShared(quest, tags, null, quest.type.highlightedElementsRadius)
1107+
}
1108+
1109+
private fun showHighlightedElementsShared(
1110+
quest: Quest,
1111+
tags: Map<String, String>,
1112+
element: Element?,
1113+
highlightedElementsRadius: Double,
1114+
) {
1115+
val bbox = quest.geometry.bounds.enlargedBy(highlightedElementsRadius)
10451116
var mapData: MapDataWithGeometry? = null
10461117

10471118
fun getMapData(): MapDataWithGeometry {
@@ -1050,11 +1121,11 @@ class MainActivity :
10501121
return data
10511122
}
10521123

1053-
val levels = parseLevelsOrNull(element.tags)
1124+
val levels = parseLevelsOrNull(tags)
10541125

10551126
lifecycleScope.launch(Dispatchers.Default) {
10561127
val elements = withContext(Dispatchers.IO) {
1057-
quest.type.getHighlightedElements(element, ::getMapData)
1128+
quest.type.getHighlightedElementsGeneric(element, ::getMapData)
10581129
}
10591130

10601131
val markers = elements.mapNotNull { e ->
@@ -1064,7 +1135,7 @@ class MainActivity :
10641135
val eLevels = parseLevelsOrNull(e.tags)
10651136
if (!levels.levelsIntersect(eLevels)) return@mapNotNull null
10661137
// include only elements with the same layer, if any
1067-
if (element.tags["layer"] != e.tags["layer"]) return@mapNotNull null
1138+
if (tags["layer"] != e.tags["layer"]) return@mapNotNull null
10681139

10691140
val geometry = mapData?.getGeometry(e.type, e.id) ?: return@mapNotNull null
10701141
val icon = getIcon(featureDictionary.value, e)

app/src/commonMain/kotlin/de/westnordost/streetcomplete/data/osm/osmquests/OsmElementQuestType.kt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,12 @@ interface OsmElementQuestType<T> : QuestType, ElementEditType {
8787
* any misunderstandings which element is meant that far apart. */
8888
val highlightedElementsRadius: Double get() = 30.0
8989

90+
override fun getHighlightedElementsGeneric(element: Element?, getMapData: () -> MapDataWithGeometry): Sequence<Element> {
91+
return getHighlightedElements(element!!, getMapData)
92+
}
93+
94+
abstract fun getHighlightedElements(element: Element, getMapData: () -> MapDataWithGeometry) : Sequence<Element>
95+
9096
/** Applies the data from [answer] to the element that has last been edited at [timestampEdited]
9197
* with the given [tags] and the given [geometry].
9298
* The element is not directly modified, instead, a map of [tags] is modified */

app/src/commonMain/kotlin/de/westnordost/streetcomplete/data/osm/osmquests/OsmFilterQuestType.kt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,10 @@ abstract class OsmFilterQuestType<T> : OsmElementQuestType<T> {
1818
mapData.filter(elementFilter).asIterable()
1919

2020
override fun isApplicableTo(element: Element) = filter.matches(element)
21+
22+
override fun getHighlightedElementsGeneric(element: Element?, getMapData: () -> MapDataWithGeometry): Sequence<Element> {
23+
return getHighlightedElements(element!!, getMapData)
24+
}
25+
26+
abstract fun getHighlightedElements(element: Element, getMapData: () -> MapDataWithGeometry) : Sequence<Element>
2127
}

app/src/commonMain/kotlin/de/westnordost/streetcomplete/data/quest/OsmCreateElementQuestType.kt

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,10 @@ package de.westnordost.streetcomplete.data.quest
22

33
import de.westnordost.streetcomplete.data.osm.edits.ElementEditType
44

5-
interface OsmCreateElementQuestType<T> : QuestType, ElementEditType
5+
interface OsmCreateElementQuestType<T> : QuestType, ElementEditType {
6+
/** The radius in which certain elements should be shown (see getHighlightedElements).
7+
* 30m is the default because this is about "across this large street". There shouldn't be
8+
* any misunderstandings which element is meant that far apart. */
9+
// TODO: highlightedElementsRadius and its comment duplicates OsmElementQuestType entry: should it be moved higher? Into interface? This does not apply to Note quests
10+
val highlightedElementsRadius: Double get() = 30.0
11+
}

app/src/commonMain/kotlin/de/westnordost/streetcomplete/data/quest/QuestType.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,5 +25,5 @@ interface QuestType : EditType {
2525
/** Elements that should be highlighted on the map alongside the selected one because they
2626
* provide context for the given element. For example, nearby benches should be shown when
2727
* answering a question for a bench so the user knows which of the benches is meant. */
28-
fun getHighlightedElements(element: Element, getMapData: () -> MapDataWithGeometry): Sequence<Element> = emptySequence()
28+
fun getHighlightedElementsGeneric(element: Element?, getMapData: () -> MapDataWithGeometry): Sequence<Element> = emptySequence()
2929
}

0 commit comments

Comments
 (0)